DragonFlyBSD Kernel Audit
DF-2745 / fix.diff
← back to finding ↓ download raw
--- 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;