DragonFlyBSD Kernel Audit
DF-2816 / fix.diff
← back to finding ↓ download raw
--- a/sys/kern/kern_shutdown.c	2026-09-01 17:11:51.508420939 +0000
+++ b/sys/kern/kern_shutdown.c	2026-09-01 17:12:00.456309347 +0000
@@ -72,6 +72,7 @@
 
 #include <sys/thread2.h>
 #include <sys/buf2.h>
+#include <sys/spinlock2.h>
 #include <sys/mplock2.h>
 
 #include <machine/cpu.h>
@@ -135,6 +136,8 @@
 
 __read_mostly int dumping;		/* system is dumping */
 static struct dumperinfo dumper;	/* selected dumper */
+static struct spinlock dumper_lock = SPINLOCK_INITIALIZER(dumper_lock,
+						          "dumper");
 
 __read_frequently globaldata_t panic_cpu_gd;	/* used in lock assertion */
 struct lwkt_tokref panic_tokens[LWKT_MAXTOKENS];
@@ -288,7 +291,19 @@
 	 * process is stopped.
 	 */
 	if (panicstr == NULL) {
-		shutdown_cleanup_proc(curproc);
+		/*
+		 * Only clean up the calling process when it is single
+		 * threaded (DF-2816).  shutdown_cleanup_proc() closes all
+		 * of its fds, NULLs its fd_ncdir/fd_nrdir namecache
+		 * handles and removes its user address space while its
+		 * sibling threads may still be running on other CPUs.  A
+		 * sibling that then enters the kernel (path lookup,
+		 * copyin/copyout) dereferences the NULL-ed handle or
+		 * touches the removed map and takes a fatal kernel-mode
+		 * page fault: panic instead of a clean reboot.
+		 */
+		if (curproc == NULL || curproc->p_nthreads < 2)
+			shutdown_cleanup_proc(curproc);
 		shutdown_cleanup_proc(&proc0);
 		if (initproc) {
 			if (initproc != curproc) {
@@ -948,21 +963,30 @@
 int
 set_dumper(struct dumperinfo *di)
 {
+	int error = 0;
+
+	/*
+	 * Serialize registration/clearing against other set_dumper()
+	 * calls and against dumpsys() taking its snapshot (DF-2815).
+	 */
+	spin_lock(&dumper_lock);
 	if (di == NULL) {
 		bzero(&dumper, sizeof(dumper));
-		return 0;
+	} else if (dumper.dumper != NULL) {
+		error = EBUSY;
+	} else {
+		dumper = *di;
 	}
+	spin_unlock(&dumper_lock);
 
-	if (dumper.dumper != NULL)
-		return (EBUSY);
-
-	dumper = *di;
-	return 0;
+	return (error);
 }
 
 void
 dumpsys(void)
 {
+	struct dumperinfo di;
+
 #if defined (_KERNEL_VIRTUAL)
 	/* vkernels don't support dumps */
 	kprintf("vkernels don't support dumps\n");
@@ -977,9 +1001,19 @@
 	 *      dependent in case we ever add a platform which does not share
 	 *      the same dumpsys() code, such as arm.
 	 */
+	/*
+	 * Take a stable snapshot of the dumper info under the lock so a
+	 * concurrent set_dumper()/clear cannot rip the device out from
+	 * under md_dumpsys() mid-dump (DF-2815).
+	 */
+	spin_lock(&dumper_lock);
 	if (dumper.dumper != NULL && !dumping) {
-		dumping++;
-		md_dumpsys(&dumper);
+		di = dumper;
+		dumping = 1;
+		spin_unlock(&dumper_lock);
+		md_dumpsys(&di);
+	} else {
+		spin_unlock(&dumper_lock);
 	}
 }