NetBSD Problem Report #59175
From wiz@netbsd.org Thu Mar 13 21:25:10 2025
Return-Path: <wiz@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256
client-signature RSA-PSS (2048 bits) client-digest SHA256)
(Client CN "mail.NetBSD.org", Issuer "mail.NetBSD.org CA" (not verified))
by mollari.NetBSD.org (Postfix) with ESMTPS id F29C61A9239
for <gnats-bugs@gnats.NetBSD.org>; Thu, 13 Mar 2025 21:25:09 +0000 (UTC)
Message-Id: <20250313212359.E4A9E2EBBAD9@exadelic.gatalith.at>
Date: Thu, 13 Mar 2025 22:23:59 +0100 (CET)
From: Thomas Klausner <wiz@NetBSD.org>
Reply-To: Thomas Klausner <wiz@NetBSD.org>
To: gnats-bugs@NetBSD.org
Subject: posix_spawn hang, hanging other process too
X-Send-Pr-Version: 3.95
>Number: 59175
>Notify-List: riastradh@NetBSD.org, spz@NetBSD.org
>Category: kern
>Synopsis: posix_spawn hang, hanging other process too
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: kern-bug-people
>State: closed
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Thu Mar 13 21:30:00 +0000 2025
>Closed-Date: Fri May 09 14:08:33 +0000 2025
>Last-Modified: Sat Jun 27 17:25:06 +0000 2026
>Originator: Thomas Klausner
>Release: NetBSD 10.99.12
>Organization:
>Environment:
Architecture: x86_64
Machine: amd64
>Description:
sysutils/htop didn't exit, eating 100% CPU, and was not killable, not even
with -9, not even as root.
It seems to be related to second process, a ninja process that's dying
and hanging in posix_spawn.
wiz 11928 99.3 0.0 15720 2484 pts/7- ON 12:38nachm. 559:26.17 htop
2000 12110 0.0 0.0 15720 2484 ? R 12:38nachm. 559:26.17 (ninja)
htop's backtrace looks nearly always like this:
crash> trace/t 2E98
trace: pid 11928 lid 11928 at 0xffffc1a49a32f8d0
pmap_update() at pmap_update+0x1b
uvm_fault_upper_enter() at uvm_fault_upper_enter+0x1d2
uvm_fault_internal() at uvm_fault_internal+0xa95
trap() at trap+0x457
--- trap (number 6) ---
kcopy() at kcopy+0x15
sysctl_kern_proc_args() at sysctl_kern_proc_args+0x46f
sysctl_dispatch() at sysctl_dispatch+0xae
sys___sysctl() at sys___sysctl+0xc5
syscall() at syscall+0x112
--- syscall (number 202) ---
syscall+0x112:
The ninja process information:
crash> show proc 0t12110
ninja: pid 12110 proc ffffa7781a4bb4c0 vmspace/map ffffa780efa93900 flags 4000
lwp 12110 ffffa77c23c78800 pcb ffffc1a48c5c0000
stat 3 flags 0 cpu 13 pri 26 ref 0
wmesg pspawn wchan ffffa77e5d16e270
(gdb) print *(struct proc *)0xffffa7781a4bb4c0 $2 = {p_list = {le_next = 0xffffa7735bb983c0, le_prev = 0xffffa7652b39fb40}, p_lock = 0xffffa76f1e9837c0, p_waitcv = {cv_opaque = {0x0, 0xffffffff8141e952}}, p_lwpcv = {cv_opaque = {0x0, 0xffffffff81413c3c}}, p_cred = 0xffffa780f8e9f040, p_fd = 0xffffa77384cb2b00, p_cwdi = 0xffffa7797ee61400, p_stats = 0xffffa775cad8f280, p_limit = 0xffffa7765cfecb80, p_vmspace = 0xffffa780efa93900, p_sigacts = 0xffffa770ad48d310, p_aio = 0x0, p_mqueue_cnt = 0, p_specdataref = {specdataref_container = 0x0, specdataref_lock = {u = {mtxa_owner = 0, s = {mtxs_dummy = 0 '\000', mtxs_ipl = {_ipl = 0 '\000'}, mtxs_lock = 0 '\000', mtxs_unused = 0 '\000'}}}}, p_exitsig = 20, p_flag = 16384, p_sflag = 0, p_stflag = 0, p_slflag = 0, p_stat = 2 '\002', p_lflag = 0 '\000', p_trace_enabled = 0 '\000', p_pad1 = "\000\000", p_pid = 12110, p_pglist = {le_next = 0xffffa77795303c40, le_prev = 0xffffa773f5c1d9c0}, p_pptr = 0xffffa7735bb98040, p_sibling = {le_next = 0xffff
a7652b39fb40, le_prev = 0xffffa7735bb98118}, p_children = { lh_first = 0xffffa7764380c400}, p_lwps = {lh_first = 0xffffa77c23c78800}, p_raslist = 0x0, p_nlwps = 1, p_nzlwps = 0, p_nrlwps = 1, p_nlwpwait = 0, p_ndlwps = 0, p_nstopchild = 15, p_waited = 0, p_zomblwp = 0x0, p_vforklwp = 0x0, p_sched_info = 0x0, p_estcpu = 0, p_estcpu_inherited = 36864, p_forktime = 348042, p_pctcpu = 0, p_opptr = 0x0, p_timers = 0x0, p_rtime = {sec = 0, frac = 0}, p_uticks = 97, p_sticks = 53, p_iticks = 0, p_xutime = 1121300, p_xstime = 607370, p_traceflag = 0, p_tracep = 0x0, p_textvp = 0xffffa76612be4540, p_emul = 0xffffffff8188afc0 <emul_netbsd>, p_emuldata = 0x0, p_execsw = 0xffffffff81887560 <exec_elf64_execsw>, p_klist = {slh_first = 0x0}, p_sigwaiters = {lh_first = 0x0}, p_sigpend = {sp_info = {tqh_first = 0x0, tqh_last = 0xffffa7781a4bb680}, sp_set = {__bits = {0, 0, 0, 0}}}, p_lwpctl = 0x0, p_ppid = 1, p_oppid = 27349, p_path = 0xffffa76fcf38cec0 "/usr/pkg/bin/ninja", p_sigctx = {ps_info = {_
signo = 0, _code = 0, _errno = 0, _pad = 0, _reason = {_rt = {_pid = 0, _uid = 0, _value = {sival_int = 0, sival_ptr = 0x0}}, _child = {_pid = 0, _uid = 0, _status = 0, _utime = 0, _stime = 0}, _fault = {_addr = 0x0, _trap = 0, _trap2 = 0, _trap3 = 0}, _poll = {_band = 0, _fd = 0}, _syscall = {_sysnum = 0, _retval = {0, 0}, _error = 0, _args = {0, 0, 0, 0, 0, 0, 0, 0}}, _ptrace_state = {_pe_report_event = 0, _option = {_pe_other_pid = 0, _pe_lwp = 0}}}}, ps_lwp = 0, ps_faked = false, ps_sigcode = 0x0, ps_sigignore = {__bits = {2554888192, 0, 0, 0}}, ps_sigcatch = {__bits = {16387, 0, 0, 0}}, ps_sigpass = {__bits = {0, 0, 0, 0}}}, p_nice = 20 '\024', p_comm = "ninja\000build\000\000\000\000\000", p_pgrp = 0xffffa773f5c1d9c0, p_psstrp = 140187723116512, p_pax = 3, p_xexit = 0, p_xsig = 0, p_acflag = 0, p_md = {md_flags = 0, md_syscall = 0xffffffff805b7504 <syscall>}, p_stackbase = 140187720364032, p_dtrace = 0xffffa76726712b80, p_auxlock = {u = {mtxa_owner = 0, s = {mtxs_dummy = 0 '\0
00', mtxs_ipl = {_ipl = 0 '\000'}, mtxs_lock = 0 '\000', mtxs_unused = 0 '\000'}}}, p_stmutex = {u = {mtxa_owner = 2049, s = {mtxs_dummy = 1 '\001', mtxs_ipl = {_ipl = 8 '\b'}, mtxs_lock = 0 '\000', mtxs_unused = 0 '\000'}}}, p_reflock = { rw_owner = 18446646750350903300}}
crash> bt/t 0t12110
trace: pid 12110 lid 12110 at 0xffffc1a48c5c4e20
sleepq_block() at sleepq_block+0xee
cv_wait() at cv_wait+0xd4
do_posix_spawn() at do_posix_spawn+0x660
sys_posix_spawn() at sys_posix_spawn+0x1f2
syscall() at syscall+0x112
--- syscall (number 474) ---
syscall+0x112:
>How-To-Repeat:
Start htop, get unlucky.
>Fix:
Fix posix_spawn().
>Release-Note:
>Audit-Trail:
From: Taylor R Campbell <riastradh@NetBSD.org>
To: wiz@NetBSD.org
Cc: gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org
Subject: Re: kern/59175: posix_spawn hang, hanging other process too
Date: Thu, 13 Mar 2025 21:54:58 +0000
This is a multi-part message in MIME format.
--=_ml7q+P8YFLTA7qSAPcDHHBHCSIXmg0yj
The attached patch should fix one of the problems that led into this.
However, the hypothesis justifying it doesn't explain all the symptoms
you're seeing, so there's more to investigate -- still not clear why
sysctl_kern_proc_args is repeatedly trapping.
--=_ml7q+P8YFLTA7qSAPcDHHBHCSIXmg0yj
Content-Type: text/plain; charset="ISO-8859-1"; name="pr59175-spawnwaitcondvarabuse"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment; filename="pr59175-spawnwaitcondvarabuse.patch"
# HG changeset patch
# User Taylor R Campbell <riastradh@NetBSD.org>
# Date 1741902749 0
# Thu Mar 13 21:52:29 2025 +0000
# Branch trunk
# Node ID 6be52ee843dae2b1bfd85f8c4feb856dc4229fb2
# Parent e446c5e13d047e0488bd251fe8405018b430338c
# EXP-Topic riastradh-pr59175-spawnwait
posix_spawn(2): Fix race between parent and child.
This was an embarrassing misuse of condition variables.
PR kern/59175: posix_spawn hang, hanging other process too
(Doesn't resolve everything about the symptoms but this was obviously
a bug that needed fixing.)
diff -r e446c5e13d04 -r 6be52ee843da sys/kern/kern_exec.c
--- a/sys/kern/kern_exec.c Tue Mar 11 17:46:12 2025 +0000
+++ b/sys/kern/kern_exec.c Thu Mar 13 21:52:29 2025 +0000
@@ -281,6 +281,7 @@ struct spawn_exec_data {
kcondvar_t sed_cv_child_ready;
kmutex_t sed_mtx_child;
int sed_error;
+ bool sed_child_ready;
volatile uint32_t sed_refcnt;
};
=20
@@ -2347,6 +2348,9 @@ spawn_return(void *arg)
&& rw_tryenter(&exec_lock, RW_READER)) {
parent_is_waiting =3D false;
mutex_enter(&spawn_data->sed_mtx_child);
+ KASSERT(!spawn_data->sed_child_ready);
+ spawn_data->sed_error =3D 0;
+ spawn_data->sed_child_ready =3D true;
cv_signal(&spawn_data->sed_cv_child_ready);
mutex_exit(&spawn_data->sed_mtx_child);
}
@@ -2376,6 +2380,9 @@ spawn_return(void *arg)
=20
if (parent_is_waiting) {
mutex_enter(&spawn_data->sed_mtx_child);
+ KASSERT(!spawn_data->sed_child_ready);
+ spawn_data->sed_error =3D 0;
+ spawn_data->sed_child_ready =3D true;
cv_signal(&spawn_data->sed_cv_child_ready);
mutex_exit(&spawn_data->sed_mtx_child);
}
@@ -2409,7 +2416,9 @@ spawn_return(void *arg)
if (parent_is_waiting) {
/* pass error to parent */
mutex_enter(&spawn_data->sed_mtx_child);
+ KASSERT(!spawn_data->sed_child_ready);
spawn_data->sed_error =3D error;
+ spawn_data->sed_child_ready =3D true;
cv_signal(&spawn_data->sed_cv_child_ready);
mutex_exit(&spawn_data->sed_mtx_child);
} else {
@@ -2814,7 +2823,10 @@ do_posix_spawn(struct lwp *l1, pid_t *pi
mutex_exit(p2->p_lock);
mutex_exit(&proc_lock);
=20
- cv_wait(&spawn_data->sed_cv_child_ready, &spawn_data->sed_mtx_child);
+ while (!spawn_data->sed_child_ready) {
+ cv_wait(&spawn_data->sed_cv_child_ready,
+ &spawn_data->sed_mtx_child);
+ }
error =3D spawn_data->sed_error;
mutex_exit(&spawn_data->sed_mtx_child);
spawn_exec_data_release(spawn_data);
--=_ml7q+P8YFLTA7qSAPcDHHBHCSIXmg0yj--
From: Taylor R Campbell <riastradh@NetBSD.org>
To: wiz@NetBSD.org
Cc: gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org
Subject: Re: kern/59175: posix_spawn hang, hanging other process too
Date: Fri, 14 Mar 2025 01:23:53 +0000
This is a multi-part message in MIME format.
--=_UO/DuVWULAgwAF6z8mwyycn6j85jhood
The attached program appears to reproduce the issue.
make waitrace
./waitraice &
while :; do kill -INFO %1; sleep 1; done
At some point, it will stop printing numbers because it is stuck.
This is probably some deadlock between some combination of p_reflock,
exec_lock, proc_lock, and who knows what other horrible lock
shenanigans posix_spawn gets itself into.
--=_UO/DuVWULAgwAF6z8mwyycn6j85jhood
Content-Type: text/plain; charset="ISO-8859-1"; name="waitrace"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment; filename="waitrace.c"
#include <sys/sysctl.h>
#include <sys/wait.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <spawn.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
unsigned long long n;
static void
info(int signo)
{
char buf[32];
snprintf_ss(buf, sizeof(buf), "%llu\n", n);
(void)write(STDOUT_FILENO, buf, strlen(buf));
}
int
main(int argc, char **argv)
{
static char buf[65536];
int mib[] =3D {CTL_KERN, KERN_PROC_ARGS, /*pid*/-1, KERN_PROC_NARGV};
sigset_t mask, omask;
if (argc =3D=3D 2) {
ssize_t nread;
nread =3D read(STDIN_FILENO, (char[]){0}, 1);
if (nread =3D=3D -1)
err(1, "read");
if (nread =3D=3D 0)
errx(1, "premature eof");
return 0;
}
if (sigemptyset(&mask) =3D=3D -1)
err(1, "sigemptyset");
if (sigaddset(&mask, SIGINFO) =3D=3D -1)
err(1, "sigaddset");
if (signal(SIGINFO, &info) =3D=3D SIG_ERR)
err(1, "signal(SIGINFO)");
for (;;) {
char *const nargv[] =3D {argv[0], "stuff", NULL};
int waitpipe[2];
posix_spawn_file_actions_t fa;
pid_t pid;
size_t buflen;
int status, error;
if (pipe2(waitpipe, O_CLOEXEC) =3D=3D -1)
err(1, "pipe2");
error =3D posix_spawn_file_actions_init(&fa);
if (error)
err(1, "posix_spawn_file_actions_init");
error =3D posix_spawn_file_actions_adddup2(&fa, waitpipe[0],
STDIN_FILENO);
if (error)
err(1, "posix_spawn_file_actions_adddup2");
error =3D posix_spawn(&pid, nargv[0], &fa, NULL, nargv, NULL);
if (error)
err(1, "posix_spawn");
mib[2] =3D (int)pid;
buflen =3D sizeof(buf);
while (sysctl(mib, __arraycount(mib), buf, &buflen, NULL, 0)
=3D=3D -1) {
if (errno =3D=3D EBUSY)
continue;
err(1, "sysctl kern.proc_args.%d.nargv", mib[2]);
}
if (close(waitpipe[0]) =3D=3D -1)
err(1, "close");
if (write(waitpipe[1], (char[]){0}, 1) =3D=3D -1)
err(1, "write");
if (close(waitpipe[1]))
err(1, "write");
error =3D posix_spawn_file_actions_destroy(&fa);
if (error)
err(1, "posix_spawn_file_actions_destroy");
if (waitpid(pid, &status, 0) =3D=3D -1)
err(1, "waitpid");
if (status !=3D 0)
errx(1, "status=3D0x%x", status);
if (sigprocmask(SIG_BLOCK, &mask, &omask) =3D=3D -1)
err(1, "sigprocmask(SIG_BLOCK)");
n++;
if (sigprocmask(SIG_SETMASK, &omask, NULL) =3D=3D -1)
err(1, "sigprocmask(SIG_SETMASK)");
}
printf("%llu\n", n);
fflush(stdout);
return ferror(stdout);
}
--=_UO/DuVWULAgwAF6z8mwyycn6j85jhood--
From: Thomas Klausner <wiz@NetBSD.org>
To: Taylor R Campbell <riastradh@NetBSD.org>
Cc: gnats-bugs@NetBSD.org
Subject: Re: kern/59175: posix_spawn hang, hanging other process too
Date: Fri, 14 Mar 2025 08:08:34 +0100
On Fri, Mar 14, 2025 at 01:23:53AM +0000, Taylor R Campbell wrote:
> The attached program appears to reproduce the issue.
>
> make waitrace
> ./waitraice &
> while :; do kill -INFO %1; sleep 1; done
>
> At some point, it will stop printing numbers because it is stuck.
Just to confirm, I ran this test just now and saw numbers go up to for
a couple minutes then it stopped.
I have a
28394 wiz 23 5 11M 1484K CPU/1 13:37 99.17% 99.17% waitrace
process now, and I guess a stuck zsh (but I have a couple zsh
instances running, so I don't know which one is running the kill(1) loop).
Thomas
From: mlelstv@serpens.de (Michael van Elst)
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: kern/59175: posix_spawn hang, hanging other process too
Date: Fri, 14 Mar 2025 11:07:28 -0000 (UTC)
gnats-admin@NetBSD.org ("Thomas Klausner via gnats") writes:
> On Fri, Mar 14, 2025 at 01:23:53AM +0000, Taylor R Campbell wrote:
> > The attached program appears to reproduce the issue.
> >
> > make waitrace
> > ./waitraice &
> > while :; do kill -INFO %1; sleep 1; done
> >
> > At some point, it will stop printing numbers because it is stuck.
>
> Just to confirm, I ran this test just now and saw numbers go up to for
> a couple minutes then it stopped.
The test ran for several hours on a Sandy Bridge (-current) and
Haswell (netbsd-10) system without locking up.
On a Zen1 Ryzen (-current) it stopped after about 20 minutes. Apparently
sysctl_kern_proc_args tries to page in something and loops infinitely.
From: Thomas Klausner <wiz@NetBSD.org>
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: kern/59175: posix_spawn hang, hanging other process too
Date: Fri, 14 Mar 2025 13:01:18 +0100
My test system is an AMD Ryzen 9. So perhaps the bug is at least
partially AMD-specific?
Thomas
From: Taylor R Campbell <riastradh@NetBSD.org>
To: Thomas Klausner <wiz@NetBSD.org>
Cc: gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org,
martin@NetBSD.org, chs@NetBSD.org
Subject: Re: kern/59175: posix_spawn hang, hanging other process too
Date: Fri, 14 Mar 2025 14:28:38 +0000
So here's what I think is happening:
1. do_posix_spawn in the parent p1 creates a process p2 with
proc_alloc, which has:
- p2->p_stat = SIDL (can't be looked up with proc_find)
- p2->p_vmspace = &vmspace0 (initial vmspace, kernel's) or NULL
- p2->p_psstrp = garbage from recycled struct proc or NULL
In particular, if p2 is freshly allocated, it will have null
p_vmspace and p_psstrp; if it was recycled, p_vmspace will have
been set to proc0.p_vmspace=&vmspace0 shortly before recycling
in uvm_proc_exit:
https://nxr.netbsd.org/xref/src/sys/uvm/uvm_glue.c?r=1.182#414
And p_psstrp will have been left untouched.
2. do_posix_spawn in the parent creates a lwp in p2 to run
spawn_return, sets p2->p_stat = SACTIVE (so it _can_ be looked up
with proc_find), makes the lwp runnable, and waits for it to ack
(sed_cv_child_ready).
3. spawn_return in the child p2 acks immediately (sed_child_cv_ready)
because neither POSIX_SPAWN_RETURNERROR nor POSIX_SPAWN_SETPGROUP
is set.
4. do_posix_spawn in the parent returns the child pid to the caller.
5. parent tries sysctl kern.proc_args.<childpid>.argv or something
which takes p2->p_reflock and does copyin_psstrings(p2, ...) which
copies in p2->p_psstrp (garbage from recycled struct proc) from
p2->p_vmspace (&vmspace0, kernel's).
I'm not sure exactly what will happen here but it seems to send the
kernel into a loop; in any case, whatever the symptom is, nothing
good can come of attempting copyin_psstrings(p2, ...) before
p2->p_vmspace and p2->p_psstrp are initialized.
6. spawn_return in the child waits for p2->p_reflock before it can
call execve_runproc to initialize p2->p_vmspace and p2->p_psstrp.
In step (5), the parent gets stuck in a loop holding p2->p_reflock
because p2->p_vmspace and p2->p_psstrp aren't initialized, while
concurrently in step (6) the child is stuck waiting for p2->p_reflock
before it can initialize p2->p_vmspace and p2->p_psstrp -- deadlock.
(The window of time between (3) and (6) is pretty small, so the parent
-- or any other process like a concurrent htop(1) -- has to be pretty
quick about invoking sysctl during that window to win the race.)
I think the general problem here is that the pid should not be usable
by anything outside (like ps(1) or top(1), via sysctl kern.proc_args
and stuff, in turn via proc_find) until execve_runproc has fully
initialized the vmspace and psstrp.
But I don't know if the child process can even run before p2->p_stat
is set to SACTIVE, at which point it can be looked up with proc_find.
Maybe we can safely defer the transition SIDL -> SACTIVE to
spawn_return, if the lwp can run even if the process is not SACTIVE.
And we should audit fork1 vs do_posix_spawn to make sure everything
that fork1 initializes is also initialized by do_posix_spawn by the
time of the SIDL -> SACTIVE transition.
Separately, the parent can't return the child pid to the caller until
that's done and the pid is usable (e.g., for waitpid(2) or kill(2)).
So the early parent-wakeup path in spawn_return is probably just not
workable.
From: Taylor R Campbell <riastradh@NetBSD.org>
To:
Cc: Thomas Klausner <wiz@NetBSD.org>
Subject: Re: kern/59175: posix_spawn hang, hanging other process too
Date: Fri, 14 Mar 2025 15:14:37 +0000
> Date: Fri, 14 Mar 2025 14:28:38 +0000
> From: Taylor R Campbell <riastradh@NetBSD.org>
>
> 1. do_posix_spawn in the parent p1 creates a process p2 with
> proc_alloc, which has:
> - p2->p_stat = SIDL (can't be looked up with proc_find)
> - p2->p_vmspace = &vmspace0 (initial vmspace, kernel's) or NULL
> - p2->p_psstrp = garbage from recycled struct proc or NULL
>
> In particular, if p2 is freshly allocated, it will have null
> p_vmspace and p_psstrp; if it was recycled, p_vmspace will have
> been set to proc0.p_vmspace=&vmspace0 shortly before recycling
> in uvm_proc_exit:
> https://nxr.netbsd.org/xref/src/sys/uvm/uvm_glue.c?r=1.182#414
Tiny correction: do_posix_spawn always initializes p2->p_vmspace to
proc0.p_vmspace, i.e., &vmspace0, so NULL is not possible here, as of
kern_exec.c rev. 1.348.
This doesn't affect the rest of the analysis, nor my tentative
conclusion that the early-parent-wakeup path is essentially unsound.
Maybe it is workable if we tweak users of p_vmspace to act as if
proc_find had failed but that sounds like a lot of work and also
dicey; surely if kill(pid, 0) works then so should anything else like
sysctls to query process stuff.
Potentially relevant historical PRs:
- https://gnats.NetBSD.org/46286 posix_spawn induced panic
- https://gnats.NetBSD.org/50330 spawn_return alters p_stat without
updating p_nstopchild
- https://gnats.NetBSD.org/54786 Panic triggered by
posix_spawn_kill_spawner test case
From: Taylor R Campbell <riastradh@NetBSD.org>
To: Thomas Klausner <wiz@NetBSD.org>
Cc: gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org,
martin@NetBSD.org, chs@NetBSD.org, dholland@NetBSD.org
Subject: Re: kern/59175: posix_spawn hang, hanging other process too
Date: Fri, 14 Mar 2025 20:05:59 +0000
This is a multi-part message in MIME format.
--=_k+R2A0jPCfl/fFvU84G4E4n02s6AxFtx
> Date: Fri, 14 Mar 2025 15:14:37 +0000
> From: Taylor R Campbell <riastradh@NetBSD.org>
>
> > Date: Fri, 14 Mar 2025 14:28:38 +0000
> > From: Taylor R Campbell <riastradh@NetBSD.org>
> >
> > 1. do_posix_spawn in the parent p1 creates a process p2 with
> > proc_alloc, which has:
> > - p2->p_stat = SIDL (can't be looked up with proc_find)
> > - p2->p_vmspace = &vmspace0 (initial vmspace, kernel's) or NULL
> > - p2->p_psstrp = garbage from recycled struct proc or NULL
> >
> > In particular, if p2 is freshly allocated, it will have null
> > p_vmspace and p_psstrp; if it was recycled, p_vmspace will have
> > been set to proc0.p_vmspace=&vmspace0 shortly before recycling
> > in uvm_proc_exit:
> > https://nxr.netbsd.org/xref/src/sys/uvm/uvm_glue.c?r=1.182#414
>
> Tiny correction: do_posix_spawn always initializes p2->p_vmspace to
> proc0.p_vmspace, i.e., &vmspace0, so NULL is not possible here, as of
> kern_exec.c rev. 1.348.
One more correction: p_psstrp is always inherited from the parent
because it lies between p_startcopy and p_endcopy, so it's never null
when copyin_psstrings tries to get at it.
So, here are two candidate patches that attempt to resolve the issue
without discarding the early-parent-wakeup path that is evidently the
primary reason for having posix_spawn as a syscall altogether instead
of a library wrapper around vfork/execve:
1. pr59175-posixspawnnullpsstr.patch sets the child's p2->p_psstrp to
null, and updates copyin_psstrings (and the compat32 version of it)
to fail with EINVAL (or maybe it should be EBUSY so it doesn't look
like a spurious invalid-argument failure shortly after process
creation) when it finds this.
I think this is reasonably low-risk, but:
(a) I don't know what other pointers into the parent's vmspace
might still exist, and be visible to other processes, before
the child finishes the exec logic -- maybe it's just the
ps_strings but I haven't checked.
(b) This adds more complications outside posix_spawn.
2. pr59175-posixspawntempinheritvmspace.patch starts by sharing the
parent's vmspace like vfork(2) does, and then switches it during
execve_runproc like execve(2) does. During that time, the child's
p2->p_psstrp reflects the parent, just like it would be with
vfork(2).
I think this is somewhat higher-risk, but:
(a) This doesn't require reviewing other possible pointers into the
parent's vmspace -- they will continue to work exactly as well
as they would with vfork(2) and execve(2), so it requires no
changes to copyin_psstrings.
(b) This brings do_posix_spawn closer to fork1, so the code has a
better chance of being shared (as suggested in PR kern/50480:
posix_spawn vs. fork code duplication).
--=_k+R2A0jPCfl/fFvU84G4E4n02s6AxFtx
Content-Type: text/plain; charset="ISO-8859-1"; name="pr59175-posixspawnnullpsstr"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment; filename="pr59175-posixspawnnullpsstr.patch"
# HG changeset patch
# User Taylor R Campbell <riastradh@NetBSD.org>
# Date 1741980237 0
# Fri Mar 14 19:23:57 2025 +0000
# Branch trunk
# Node ID 9a8ceed8653bfe797e75c766ca83593c50eecce2
# Parent 3c321704980af3b382b8e8ab493927a652a48ea9
# EXP-Topic riastradh-pr59175-spawnwait
posix_spawn(2): Block access to ps_strings until initialized.
When the child is first created, it has:
- p_vmspace set to &vmspace0 (proc0.p_vmspace) as a sentinel value
- p_psstrp inherited from the parent
It is in this state that the pid is first published -- returned to
the parent, and findable by other processes.
XXX This means that there is a short time early in the child's
lifetime during which kern.proc_args.<childpid>.nargv &c. will fail
with EINVAL. Perhaps we should inherit p_vmspace _and_ p_psstrp like
vfork(2) would, and update them simultaneously like execve(2) would.
PR kern/59175: posix_spawn hang, hanging other process too
diff -r 3c321704980a -r 9a8ceed8653b sys/compat/netbsd32/netbsd32_kern_proc=
.c
--- a/sys/compat/netbsd32/netbsd32_kern_proc.c Fri Mar 14 17:51:57 2025 +00=
00
+++ b/sys/compat/netbsd32/netbsd32_kern_proc.c Fri Mar 14 19:23:57 2025 +00=
00
@@ -109,8 +109,11 @@ static int
copyin_psstrings_32(struct proc *p, struct ps_strings *arginfo)
{
struct ps_strings32 arginfo32;
+ int error;
=20
- int error =3D copyin_proc(p, (void *)p->p_psstrp, &arginfo32,
+ if (p->p_psstrp =3D=3D 0) /* primordial spawn */
+ return EINVAL;
+ error =3D copyin_proc(p, (void *)p->p_psstrp, &arginfo32,
sizeof(arginfo32));
if (error)
return error;
diff -r 3c321704980a -r 9a8ceed8653b sys/kern/kern_exec.c
--- a/sys/kern/kern_exec.c Fri Mar 14 17:51:57 2025 +0000
+++ b/sys/kern/kern_exec.c Fri Mar 14 19:23:57 2025 +0000
@@ -2642,6 +2642,7 @@ do_posix_spawn(struct lwp *l1, pid_t *pi
memcpy(&p2->p_startcopy, &p1->p_startcopy,
(unsigned) ((char *)&p2->p_endcopy - (char *)&p2->p_startcopy));
p2->p_vmspace =3D proc0.p_vmspace;
+ p2->p_psstrp =3D 0; /* to be initialized by child later */
=20
TAILQ_INIT(&p2->p_sigpend.sp_info);
=20
diff -r 3c321704980a -r 9a8ceed8653b sys/kern/kern_proc.c
--- a/sys/kern/kern_proc.c Fri Mar 14 17:51:57 2025 +0000
+++ b/sys/kern/kern_proc.c Fri Mar 14 19:23:57 2025 +0000
@@ -2327,6 +2327,8 @@ copyin_psstrings(struct proc *p, struct=20
}
#endif /* !defined(_RUMPKERNEL) */
=20
+ if (p->p_psstrp =3D=3D 0) /* primordial spawn */
+ return EINVAL;
return copyin_proc(p, (void *)p->p_psstrp, arginfo, sizeof(*arginfo));
}
=20
--=_k+R2A0jPCfl/fFvU84G4E4n02s6AxFtx
Content-Type: text/plain; charset="ISO-8859-1"; name="pr59175-posixspawntempinheritvmspace"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment; filename="pr59175-posixspawntempinheritvmspace.patch"
# HG changeset patch
# User Taylor R Campbell <riastradh@NetBSD.org>
# Date 1741982594 0
# Fri Mar 14 20:03:14 2025 +0000
# Branch trunk
# Node ID 0769a64ad876303f34f46e14ba424f9a0ffb90dc
# Parent 3c321704980af3b382b8e8ab493927a652a48ea9
# EXP-Topic riastradh-pr59175-spawnwait
posix_spawn(2): Share parent's VM space temporarily.
This is just like vfork(2), and this avoids special cases in other
parts of the logic. This is mainly for ps_strings access.
PR kern/59175: posix_spawn hang, hanging other process too
diff -r 3c321704980a -r 0769a64ad876 sys/kern/kern_exec.c
--- a/sys/kern/kern_exec.c Fri Mar 14 17:51:57 2025 +0000
+++ b/sys/kern/kern_exec.c Fri Mar 14 20:03:14 2025 +0000
@@ -1245,14 +1245,9 @@ execve_runproc(struct lwp *l, struct exe
* until we have awoken the parent below, or it will defeat
* lazy pmap switching (on x86).
*/
- if (is_spawn)
- uvmspace_spawn(l, epp->ep_vm_minaddr,
- epp->ep_vm_maxaddr,
- epp->ep_flags & EXEC_TOPDOWN_VM);
- else
- uvmspace_exec(l, epp->ep_vm_minaddr,
- epp->ep_vm_maxaddr,
- epp->ep_flags & EXEC_TOPDOWN_VM);
+ uvmspace_exec(l, epp->ep_vm_minaddr,
+ epp->ep_vm_maxaddr,
+ epp->ep_flags & EXEC_TOPDOWN_VM);
vm =3D p->p_vmspace;
=20
vm->vm_taddr =3D (void *)epp->ep_taddr;
@@ -2641,7 +2636,6 @@ do_posix_spawn(struct lwp *l1, pid_t *pi
(unsigned) ((char *)&p2->p_endzero - (char *)&p2->p_startzero));
memcpy(&p2->p_startcopy, &p1->p_startcopy,
(unsigned) ((char *)&p2->p_endcopy - (char *)&p2->p_startcopy));
- p2->p_vmspace =3D proc0.p_vmspace;
=20
TAILQ_INIT(&p2->p_sigpend.sp_info);
=20
@@ -2732,8 +2726,15 @@ do_posix_spawn(struct lwp *l1, pid_t *pi
*/
p2->p_stats =3D pstatscopy(p1->p_stats);
=20
- /* copy over machdep flags to the new proc */
- cpu_proc_fork(p1, p2);
+ /*
+ * Set up the new process address space.
+ *
+ * Initially, we act like vfork(2) in sharing the address
+ * space, so that the (inherited) ps_strings will be preserved.
+ * The child will soon replace its address space in
+ * execve_runproc, as if by execve(2).
+ */
+ uvm_proc_fork(p1, p2, /*shared*/true);
=20
/*
* Prepare remaining parts of spawn data
diff -r 3c321704980a -r 0769a64ad876 sys/uvm/uvm_extern.h
--- a/sys/uvm/uvm_extern.h Fri Mar 14 17:51:57 2025 +0000
+++ b/sys/uvm/uvm_extern.h Fri Mar 14 20:03:14 2025 +0000
@@ -731,7 +731,6 @@ struct vmspace *uvmspace_alloc(vaddr_t,
void uvmspace_init(struct vmspace *, struct pmap *,
vaddr_t, vaddr_t, bool);
void uvmspace_exec(struct lwp *, vaddr_t, vaddr_t, bool);
-void uvmspace_spawn(struct lwp *, vaddr_t, vaddr_t, bool);
struct vmspace *uvmspace_fork(struct vmspace *);
void uvmspace_addref(struct vmspace *);
void uvmspace_free(struct vmspace *);
diff -r 3c321704980a -r 0769a64ad876 sys/uvm/uvm_map.c
--- a/sys/uvm/uvm_map.c Fri Mar 14 17:51:57 2025 +0000
+++ b/sys/uvm/uvm_map.c Fri Mar 14 20:03:14 2025 +0000
@@ -4259,27 +4259,6 @@ uvmspace_unshare(struct lwp *l)
=20
=20
/*
- * uvmspace_spawn: a new process has been spawned and needs a vmspace
- */
-
-void
-uvmspace_spawn(struct lwp *l, vaddr_t start, vaddr_t end, bool topdown)
-{
- struct proc *p =3D l->l_proc;
- struct vmspace *nvm;
-
-#ifdef __HAVE_CPU_VMSPACE_EXEC
- cpu_vmspace_exec(l, start, end);
-#endif
-
- nvm =3D uvmspace_alloc(start, end, topdown);
- kpreempt_disable();
- p->p_vmspace =3D nvm;
- pmap_activate(l);
- kpreempt_enable();
-}
-
-/*
* uvmspace_exec: the process wants to exec a new program
*/
=20
--=_k+R2A0jPCfl/fFvU84G4E4n02s6AxFtx--
From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/59175 CVS commit: src/sys/kern
Date: Sat, 15 Mar 2025 12:11:09 +0000
Module Name: src
Committed By: riastradh
Date: Sat Mar 15 12:11:09 UTC 2025
Modified Files:
src/sys/kern: kern_exec.c
Log Message:
posix_spawn(2): Fix race between parent and child.
This was an embarrassing misuse of condition variables.
PR kern/59175: posix_spawn hang, hanging other process too
PR kern/52634: possible unhandled spurious wakeup in posix_spawn
(This only resolves one of the symptoms wiz had in PR 59175; there is
also another issue involved with p_vmspace and p_psstrp.)
To generate a diff of this commit:
cvs rdiff -u -r1.525 -r1.526 src/sys/kern/kern_exec.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: Chuck Silvers <chuq@chuq.com>
To: Taylor R Campbell <riastradh@NetBSD.org>
Cc: Thomas Klausner <wiz@NetBSD.org>, gnats-bugs@NetBSD.org,
netbsd-bugs@NetBSD.org, martin@NetBSD.org, dholland@NetBSD.org
Subject: Re: kern/59175: posix_spawn hang, hanging other process too
Date: Sun, 23 Mar 2025 13:39:31 -0700
--Eqn6h8LjFSAmGTae
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
On Fri, Mar 14, 2025 at 08:05:59PM +0000, Taylor R Campbell wrote:
> > Date: Fri, 14 Mar 2025 15:14:37 +0000
> > From: Taylor R Campbell <riastradh@NetBSD.org>
> >
> > > Date: Fri, 14 Mar 2025 14:28:38 +0000
> > > From: Taylor R Campbell <riastradh@NetBSD.org>
> > >
> > > 1. do_posix_spawn in the parent p1 creates a process p2 with
> > > proc_alloc, which has:
> > > - p2->p_stat = SIDL (can't be looked up with proc_find)
> > > - p2->p_vmspace = &vmspace0 (initial vmspace, kernel's) or NULL
> > > - p2->p_psstrp = garbage from recycled struct proc or NULL
> > >
> > > In particular, if p2 is freshly allocated, it will have null
> > > p_vmspace and p_psstrp; if it was recycled, p_vmspace will have
> > > been set to proc0.p_vmspace=&vmspace0 shortly before recycling
> > > in uvm_proc_exit:
> > > https://nxr.netbsd.org/xref/src/sys/uvm/uvm_glue.c?r=1.182#414
> >
> > Tiny correction: do_posix_spawn always initializes p2->p_vmspace to
> > proc0.p_vmspace, i.e., &vmspace0, so NULL is not possible here, as of
> > kern_exec.c rev. 1.348.
>
> One more correction: p_psstrp is always inherited from the parent
> because it lies between p_startcopy and p_endcopy, so it's never null
> when copyin_psstrings tries to get at it.
Here's an alternative patch that I prefer. It allocates a new vmspace
for the process at the time the new process is created, rather than sharing
some other vmspace temporarily. This eliminates any risk of anything
bad happening due to temporary sharing, since there isn't any sharing.
-Chuck
--Eqn6h8LjFSAmGTae
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="diff.spawn-vmspace.2"
Index: src/sys/kern/kern_exec.c
===================================================================
RCS file: /home/chs/netbsd/cvs/src/sys/kern/kern_exec.c,v
retrieving revision 1.526
diff -u -p -r1.526 kern_exec.c
--- src/sys/kern/kern_exec.c 15 Mar 2025 12:11:09 -0000 1.526
+++ src/sys/kern/kern_exec.c 22 Mar 2025 22:35:06 -0000
@@ -1245,14 +1245,8 @@ execve_runproc(struct lwp *l, struct exe
* until we have awoken the parent below, or it will defeat
* lazy pmap switching (on x86).
*/
- if (is_spawn)
- uvmspace_spawn(l, epp->ep_vm_minaddr,
- epp->ep_vm_maxaddr,
- epp->ep_flags & EXEC_TOPDOWN_VM);
- else
- uvmspace_exec(l, epp->ep_vm_minaddr,
- epp->ep_vm_maxaddr,
- epp->ep_flags & EXEC_TOPDOWN_VM);
+ uvmspace_exec(l, epp->ep_vm_minaddr, epp->ep_vm_maxaddr,
+ epp->ep_flags & EXEC_TOPDOWN_VM);
vm = p->p_vmspace;
vm->vm_taddr = (void *)epp->ep_taddr;
@@ -2641,7 +2635,14 @@ do_posix_spawn(struct lwp *l1, pid_t *pi
(unsigned) ((char *)&p2->p_endzero - (char *)&p2->p_startzero));
memcpy(&p2->p_startcopy, &p1->p_startcopy,
(unsigned) ((char *)&p2->p_endcopy - (char *)&p2->p_startcopy));
- p2->p_vmspace = proc0.p_vmspace;
+
+ /*
+ * Allocate an empty user vmspace for the new process now.
+ * The min/max and topdown parameters given here are just placeholders,
+ * the right values will be assigned in uvmspace_exec().
+ */
+ p2->p_vmspace = uvmspace_alloc(exec_vm_minaddr(VM_MIN_ADDRESS),
+ VM_MAXUSER_ADDRESS, true);
TAILQ_INIT(&p2->p_sigpend.sp_info);
Index: src/sys/uvm/uvm_extern.h
===================================================================
RCS file: /home/chs/netbsd/cvs/src/sys/uvm/uvm_extern.h,v
retrieving revision 1.233
diff -u -p -r1.233 uvm_extern.h
--- src/sys/uvm/uvm_extern.h 26 Feb 2023 07:27:14 -0000 1.233
+++ src/sys/uvm/uvm_extern.h 18 Mar 2025 14:30:19 -0000
@@ -731,7 +731,6 @@ struct vmspace *uvmspace_alloc(vaddr_t,
void uvmspace_init(struct vmspace *, struct pmap *,
vaddr_t, vaddr_t, bool);
void uvmspace_exec(struct lwp *, vaddr_t, vaddr_t, bool);
-void uvmspace_spawn(struct lwp *, vaddr_t, vaddr_t, bool);
struct vmspace *uvmspace_fork(struct vmspace *);
void uvmspace_addref(struct vmspace *);
void uvmspace_free(struct vmspace *);
Index: src/sys/uvm/uvm_map.c
===================================================================
RCS file: /home/chs/netbsd/cvs/src/sys/uvm/uvm_map.c,v
retrieving revision 1.426
diff -u -p -r1.426 uvm_map.c
--- src/sys/uvm/uvm_map.c 16 Aug 2024 11:28:01 -0000 1.426
+++ src/sys/uvm/uvm_map.c 22 Mar 2025 22:36:29 -0000
@@ -4257,28 +4257,6 @@ uvmspace_unshare(struct lwp *l)
#endif
-
-/*
- * uvmspace_spawn: a new process has been spawned and needs a vmspace
- */
-
-void
-uvmspace_spawn(struct lwp *l, vaddr_t start, vaddr_t end, bool topdown)
-{
- struct proc *p = l->l_proc;
- struct vmspace *nvm;
-
-#ifdef __HAVE_CPU_VMSPACE_EXEC
- cpu_vmspace_exec(l, start, end);
-#endif
-
- nvm = uvmspace_alloc(start, end, topdown);
- kpreempt_disable();
- p->p_vmspace = nvm;
- pmap_activate(l);
- kpreempt_enable();
-}
-
/*
* uvmspace_exec: the process wants to exec a new program
*/
@@ -4296,13 +4274,12 @@ uvmspace_exec(struct lwp *l, vaddr_t sta
cpu_vmspace_exec(l, start, end);
#endif
- map = &ovm->vm_map;
/*
- * see if more than one process is using this vmspace...
+ * If the existing is not shared but has entries, clear them out now.
*/
- if (ovm->vm_refcnt == 1
- && topdown == ((ovm->vm_map.flags & VM_MAP_TOPDOWN) != 0)) {
+ map = &ovm->vm_map;
+ if (ovm->vm_refcnt == 1 && map->nentries != 0) {
/*
* if p is the only process using its vmspace then we can safely
@@ -4344,13 +4321,22 @@ uvmspace_exec(struct lwp *l, vaddr_t sta
pmap_update(map->pmap);
KASSERT(map->header.prev == &map->header);
KASSERT(map->nentries == 0);
+ }
+
+ if (ovm->vm_refcnt == 1) {
/*
- * resize the map
+ * The vmspace is not shared and is empty.
+ * Resize the map and set topdown as appropriate.
*/
vm_map_setmin(map, start);
vm_map_setmax(map, end);
+ if (topdown) {
+ map->flags |= VM_MAP_TOPDOWN;
+ } else {
+ map->flags &= ~VM_MAP_TOPDOWN;
+ }
} else {
/*
--Eqn6h8LjFSAmGTae--
From: Taylor R Campbell <riastradh@NetBSD.org>
To: Chuck Silvers <chuq@chuq.com>
Cc: Thomas Klausner <wiz@NetBSD.org>, gnats-bugs@NetBSD.org,
netbsd-bugs@NetBSD.org, martin@NetBSD.org, dholland@NetBSD.org
Subject: Re: kern/59175: posix_spawn hang, hanging other process too
Date: Sat, 12 Apr 2025 22:41:41 +0000
> Date: Sun, 23 Mar 2025 13:39:31 -0700
> From: Chuck Silvers <chuq@chuq.com>
>=20
> Here's an alternative patch that I prefer. It allocates a new vmspace
> for the process at the time the new process is created, rather than shari=
ng
> some other vmspace temporarily. This eliminates any risk of anything
> bad happening due to temporary sharing, since there isn't any sharing.
Approach is fine by me. Some minor cosmetic issues in the patch --
let's get this committed once they're addressed:
> --- src/sys/kern/kern_exec.c 15 Mar 2025 12:11:09 -0000 1.526
> +++ src/sys/kern/kern_exec.c 22 Mar 2025 22:35:06 -0000
> ...
> @@ -2641,7 +2635,14 @@ do_posix_spawn(struct lwp *l1, pid_t *pi
> ...
> + p2->p_vmspace =3D uvmspace_alloc(exec_vm_minaddr(VM_MIN_ADDRESS),
> + VM_MAXUSER_ADDRESS, true);
Four-space indent, not alignment with `(', for continuation lines.
> --- src/sys/uvm/uvm_map.c 16 Aug 2024 11:28:01 -0000 1.426
> +++ src/sys/uvm/uvm_map.c 22 Mar 2025 22:36:29 -0000
> ...
> @@ -4296,13 +4274,12 @@ uvmspace_exec(struct lwp *l, vaddr_t sta
> cpu_vmspace_exec(l, start, end);
> #endif
> =20
> - map =3D &ovm->vm_map;
> /*
> - * see if more than one process is using this vmspace...
> + * If the existing is not shared but has entries, clear them out now.
> */
This was confusing to me at first. I think it would help to say:
/*
* If p is the only process using the vmspace, we can reuse it
* rather than allocate a new vmspace -- but we have to make
* sure it's empty first.
*/
and maybe below, under the new `if (ovm->vm_refcnt =3D=3D 1)', add
KASSERT(map->nentries =3D=3D 0) to drive in the point that it is empty
now.
> - if (ovm->vm_refcnt =3D=3D 1
> - && topdown =3D=3D ((ovm->vm_map.flags & VM_MAP_TOPDOWN) !=3D 0)) {
> + map =3D &ovm->vm_map;
> + if (ovm->vm_refcnt =3D=3D 1 && map->nentries !=3D 0) {
> =20
> /*
> * if p is the only process using its vmspace then we can safely
This comment in full reads:
/*
* if p is the only process using its vmspace then we can safely
* recycle that vmspace for the program that is being exec'd.
* But only if TOPDOWN matches the requested value for the new
* vm space!
*/
Your change invalidates the second sentence of this comment, so it
should be removed. (The first sentence is better pulled up out of the
conditional.)
State-Changed-From-To: open->needs-pullups
State-Changed-By: riastradh@NetBSD.org
State-Changed-When: Sun, 27 Apr 2025 17:41:39 +0000
State-Changed-Why:
fixed in HEAD, needs pullup-9 and pullup-10
From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/59175 CVS commit: src/sys
Date: Sun, 27 Apr 2025 17:40:56 +0000
Module Name: src
Committed By: riastradh
Date: Sun Apr 27 17:40:56 UTC 2025
Modified Files:
src/sys/kern: kern_exec.c
src/sys/uvm: uvm_extern.h uvm_map.c
Log Message:
posix_spawn(2): Allocate a new vmspace at process creation time.
This allocates a new vmspace for the process at the time the new
process is created, rather than sharing some other vmspace temporarily.
This eliminates any risk of anything bad happening due to temporary
sharing, since there isn't any sharing.
Resolves a race to where:
1. we set up the child to share proc0.p_vmspace at first,
2. another process tries to read the new child's psstrings via
kern.proc_args.<childpid>.argv or similar with the child's
p_reflock held and gets stuck in a uvm fault loop because
proc0.p_vmspace doesn't have the child's psstrings address
(inherited from the parent) mapped,
3. the child is waiting for p_reflock before it can replace its
p_vmspace or psstrings.
By allocating the vmspace up front, with no mappings in it, we avoid
exposing the child in this scenario. Minor possible downside is that
sysctl kern.proc_args.<childpid>.argv might spuriously fail with
EFAULT during this time (rather than fail with EBUSY as it does if
p_reflock is held concurrently) but that's not a particularly big
deal.
Patch and first paragraph of commit message written by chs@; minor
tweaks to comments -- and any mistakes in the analysis -- by me.
PR kern/59037: deadlock in posix_spawn
PR kern/59175: posix_spawn hang, hanging other process too
To generate a diff of this commit:
cvs rdiff -u -r1.527 -r1.528 src/sys/kern/kern_exec.c
cvs rdiff -u -r1.233 -r1.234 src/sys/uvm/uvm_extern.h
cvs rdiff -u -r1.426 -r1.427 src/sys/uvm/uvm_map.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
State-Changed-From-To: needs-pullups->pending-pullups
State-Changed-By: riastradh@NetBSD.org
State-Changed-When: Sat, 03 May 2025 15:08:26 +0000
State-Changed-Why:
pullup-10 #1109 https://releng.netbsd.org/cgi-bin/req-10.cgi?show=1109
pullup-9 #1947 https://releng.netbsd.org/cgi-bin/req-9.cgi?show=1947
From: "Martin Husemann" <martin@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/59175 CVS commit: [netbsd-10] src/sys
Date: Fri, 9 May 2025 12:59:59 +0000
Module Name: src
Committed By: martin
Date: Fri May 9 12:59:59 UTC 2025
Modified Files:
src/sys/kern [netbsd-10]: kern_exec.c
src/sys/uvm [netbsd-10]: uvm_extern.h uvm_map.c
Log Message:
Pull up following revision(s) (requested by riastradh in ticket #1109):
sys/uvm/uvm_extern.h: revision 1.234
sys/kern/kern_exec.c: revision 1.528
sys/uvm/uvm_map.c: revision 1.427
posix_spawn(2): Allocate a new vmspace at process creation time.
This allocates a new vmspace for the process at the time the new
process is created, rather than sharing some other vmspace temporarily.
This eliminates any risk of anything bad happening due to temporary
sharing, since there isn't any sharing.
Resolves a race to where:
1. we set up the child to share proc0.p_vmspace at first,
2. another process tries to read the new child's psstrings via
kern.proc_args.<childpid>.argv or similar with the child's
p_reflock held and gets stuck in a uvm fault loop because
proc0.p_vmspace doesn't have the child's psstrings address
(inherited from the parent) mapped,
3. the child is waiting for p_reflock before it can replace its
p_vmspace or psstrings.
By allocating the vmspace up front, with no mappings in it, we avoid
exposing the child in this scenario. Minor possible downside is that
sysctl kern.proc_args.<childpid>.argv might spuriously fail with
EFAULT during this time (rather than fail with EBUSY as it does if
p_reflock is held concurrently) but that's not a particularly big
deal.
Patch and first paragraph of commit message written by chs@; minor
tweaks to comments -- and any mistakes in the analysis -- by me.
PR kern/59037: deadlock in posix_spawn
PR kern/59175: posix_spawn hang, hanging other process too
To generate a diff of this commit:
cvs rdiff -u -r1.518.4.1 -r1.518.4.2 src/sys/kern/kern_exec.c
cvs rdiff -u -r1.232 -r1.232.12.1 src/sys/uvm/uvm_extern.h
cvs rdiff -u -r1.403.2.3 -r1.403.2.4 src/sys/uvm/uvm_map.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: "Martin Husemann" <martin@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/59175 CVS commit: [netbsd-9] src/sys
Date: Fri, 9 May 2025 13:04:27 +0000
Module Name: src
Committed By: martin
Date: Fri May 9 13:04:27 UTC 2025
Modified Files:
src/sys/kern [netbsd-9]: kern_exec.c
src/sys/uvm [netbsd-9]: uvm_extern.h uvm_map.c
Log Message:
Pull up following revision(s) (requested by riastradh in ticket #1947):
sys/uvm/uvm_extern.h: revision 1.234 (via patch)
sys/kern/kern_exec.c: revision 1.528 (via patch)
sys/uvm/uvm_map.c: revision 1.427 (via patch)
posix_spawn(2): Allocate a new vmspace at process creation time.
This allocates a new vmspace for the process at the time the new
process is created, rather than sharing some other vmspace temporarily.
This eliminates any risk of anything bad happening due to temporary
sharing, since there isn't any sharing.
Resolves a race to where:
1. we set up the child to share proc0.p_vmspace at first,
2. another process tries to read the new child's psstrings via
kern.proc_args.<childpid>.argv or similar with the child's
p_reflock held and gets stuck in a uvm fault loop because
proc0.p_vmspace doesn't have the child's psstrings address
(inherited from the parent) mapped,
3. the child is waiting for p_reflock before it can replace its
p_vmspace or psstrings.
By allocating the vmspace up front, with no mappings in it, we avoid
exposing the child in this scenario. Minor possible downside is that
sysctl kern.proc_args.<childpid>.argv might spuriously fail with
EFAULT during this time (rather than fail with EBUSY as it does if
p_reflock is held concurrently) but that's not a particularly big
deal.
Patch and first paragraph of commit message written by chs@; minor
tweaks to comments -- and any mistakes in the analysis -- by me.
PR kern/59037: deadlock in posix_spawn
PR kern/59175: posix_spawn hang, hanging other process too
To generate a diff of this commit:
cvs rdiff -u -r1.478.2.2 -r1.478.2.3 src/sys/kern/kern_exec.c
cvs rdiff -u -r1.213 -r1.213.6.1 src/sys/uvm/uvm_extern.h
cvs rdiff -u -r1.362.2.6 -r1.362.2.7 src/sys/uvm/uvm_map.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
State-Changed-From-To: pending-pullups->closed
State-Changed-By: riastradh@NetBSD.org
State-Changed-When: Fri, 09 May 2025 14:08:33 +0000
State-Changed-Why:
fixed in HEAD, pulled up to 9 and 10
From: "Martin Husemann" <martin@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/59175 CVS commit: [netbsd-10] src/sys/kern
Date: Sat, 27 Jun 2026 17:21:06 +0000
Module Name: src
Committed By: martin
Date: Sat Jun 27 17:21:06 UTC 2026
Modified Files:
src/sys/kern [netbsd-10]: kern_exec.c
Log Message:
Pull up following revision(s) (requested by riastradh in ticket #1283):
sys/kern/kern_exec.c: revision 1.526
posix_spawn(2): Fix race between parent and child.
This was an embarrassing misuse of condition variables.
PR kern/59175: posix_spawn hang, hanging other process too
PR kern/52634: possible unhandled spurious wakeup in posix_spawn
(This only resolves one of the symptoms wiz had in PR 59175; there is
also another issue involved with p_vmspace and p_psstrp.)
To generate a diff of this commit:
cvs rdiff -u -r1.518.4.3 -r1.518.4.4 src/sys/kern/kern_exec.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: "Martin Husemann" <martin@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/59175 CVS commit: [netbsd-9] src/sys/kern
Date: Sat, 27 Jun 2026 17:22:27 +0000
Module Name: src
Committed By: martin
Date: Sat Jun 27 17:22:27 UTC 2026
Modified Files:
src/sys/kern [netbsd-9]: kern_exec.c
Log Message:
Pull up following revision(s) (requested by riastradh in ticket #2015):
sys/kern/kern_exec.c: revision 1.526
posix_spawn(2): Fix race between parent and child.
This was an embarrassing misuse of condition variables.
PR kern/59175: posix_spawn hang, hanging other process too
PR kern/52634: possible unhandled spurious wakeup in posix_spawn
(This only resolves one of the symptoms wiz had in PR 59175; there is
also another issue involved with p_vmspace and p_psstrp.)
To generate a diff of this commit:
cvs rdiff -u -r1.478.2.3 -r1.478.2.4 src/sys/kern/kern_exec.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
>Unformatted:
(Contact us)
$NetBSD: query-full-pr,v 1.51 2026/08/10 02:28:17 riastradh Exp $
$NetBSD: gnats_config.sh,v 1.10 2026/05/13 22:00:09 riastradh Exp $
Copyright © 1994-2026
The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.