DF-2815 / fix.diff
--- 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); } } |