DF-2745 / fix.diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -96,6 +96,16 @@ static struct lwplist deadlwp_list[MAXCPU]; static struct lwkt_token deadlwp_token[MAXCPU]; +/* + * Global token serializing all mutations of the rfork(RFTHREAD) peer + * list (p_peers). exit1()'s member unlink, exit1()'s leader kill-walk + * and fork1()'s insertion all manipulate this singly linked list and + * previously did so with no common lock, allowing a lost unlink + * (a walker unlinks itself through a stale predecessor) which leaves + * the leader blocked forever in exit1() and leaves freed procs linked. + */ +struct lwkt_token peers_token = LWKT_TOKEN_INITIALIZER(peers_token); + void (*linux_task_drop_callback)(thread_t td); void (*linux_proc_drop_callback)(struct proc *p); @@ -325,18 +335,40 @@ /* are we a task leader? */ if (p == p->p_leader) { struct sysmsg sysmsg; + struct proc **ppArr; + int npids, i, cap; - sysmsg.extargs.kill.signum = SIGKILL; - q = p->p_peers; - while (q) { - sysmsg.extargs.kill.pid = q->p_pid; - /* - * The interface for kill is better - * than the internal signal - */ - sys_kill(&sysmsg, &sysmsg.extargs.kill); - q = q->p_peers; + /* + * Snapshot the peer list under peers_token with PHOLDs + * so the pids remain valid, then issue the kills outside + * the token (sys_kill->ksignal takes the victim's + * p_token; we must not hold peers_token while taking + * p_tokens because member exit1() takes peers_token + * while holding its own p_token). + */ + lwkt_gettoken(&peers_token); + cap = 0; + for (q = p->p_peers; q; q = q->p_peers) + ++cap; + lwkt_reltoken(&peers_token); + ppArr = kmalloc(sizeof(struct proc *) * (cap + 1), + M_TEMP, M_WAITOK); + npids = 0; + lwkt_gettoken(&peers_token); + for (q = p->p_peers; q && npids < cap; q = q->p_peers) { + PHOLD(q); + ppArr[npids++] = q; + } + lwkt_reltoken(&peers_token); + if (npids) { + sysmsg.extargs.kill.signum = SIGKILL; + for (i = 0; i < npids; ++i) { + sysmsg.extargs.kill.pid = ppArr[i]->p_pid; + sys_kill(&sysmsg, &sysmsg.extargs.kill); + PRELE(ppArr[i]); + } } + kfree(ppArr, M_TEMP); while (p->p_peers) tsleep((caddr_t)p, 0, "exit1", 0); } @@ -382,10 +414,12 @@ fdfree(p, NULL); if (p->p_leader->p_peers) { + lwkt_gettoken(&peers_token); q = p->p_leader; while(q->p_peers != p) q = q->p_peers; q->p_peers = p->p_peers; + lwkt_reltoken(&peers_token); wakeup((caddr_t)p->p_leader); } --- a/sys/kern/kern_fork.c +++ /tmp/opencode/df2745/sys/kern/kern_fork.c 2026-08-31 14:35:58.307886900 +0000 @@ -67,6 +67,9 @@ #include <sys/dsched.h> +/* defined in kern_exit.c; serializes the RFTHREAD p_peers list */ +extern struct lwkt_token peers_token; + static MALLOC_DEFINE(M_ATFORK, "atfork", "atfork callback"); static MALLOC_DEFINE(M_REAPER, "reaper", "process reapers"); @@ -482,8 +485,10 @@ * The process structure is addressable after this point. */ if (flags & RFTHREAD) { + lwkt_gettoken(&peers_token); p2->p_peers = p1->p_peers; p1->p_peers = p2; + lwkt_reltoken(&peers_token); p2->p_leader = p1->p_leader; } else { p2->p_leader = p2; |