DragonFlyBSD Kernel Audit
DF-2691 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
--- a/sys/kern/kern_sig.c	2026-08-30 21:40:34.351491983 +0000
+++ b/sys/kern/kern_sig.c	2026-08-30 21:40:34.371491727 +0000
@@ -2681,8 +2681,15 @@
 static void
 filt_sigdetach(struct knote *kn)
 {
-	struct proc *p = kn->kn_ptr.p_proc;
+	struct proc *p;
 
+	/*
+	 * The target process may already be gone (it detached us in
+	 * filt_signal() on NOTE_EXIT); do not touch it in that case.
+	 */
+	if (kn->kn_status & KN_DETACHED)
+		return;
+	p = kn->kn_ptr.p_proc;
 	knote_remove(&p->p_klist, kn);
 }
 
@@ -2701,5 +2708,23 @@
 		if (kn->kn_id == hint)
 			kn->kn_data++;
 	}
+	/*
+	 * Signal knotes share p->p_klist with EVFILT_PROC knotes.  When
+	 * the target process exits, exit1() broadcasts NOTE_EXIT to the
+	 * list; we used to ignore it, which left this knote linked into
+	 * p->p_klist past the reaping kfree(p, M_PROC) -- a use-after-free
+	 * once the kqueue is destroyed or the knote is deleted.  Detach on
+	 * NOTE_EXIT exactly like filt_proc() does.
+	 */
+	if ((hint & NOTE_PCTRLMASK) == NOTE_EXIT &&
+	    (kn->kn_status & KN_DETACHED) == 0) {
+		struct proc *p = kn->kn_ptr.p_proc;
+
+		PHOLD(p);
+		knote_remove(&p->p_klist, kn);
+		kn->kn_status |= KN_DETACHED;
+		kn->kn_ptr.p_proc = NULL;
+		PRELE(p);
+	}
 	return (kn->kn_data != 0);
 }