⬢ DragonFlyBSD Kernel Audit
DF-5661 / fix.diff
← back to finding ↓ download raw
--- a/sys/platform/vkernel64/platform/shutdown.c
+++ b/sys/platform/vkernel64/platform/shutdown.c
@@ -38,6 +38,8 @@
 #include <sys/types.h>
 #include <sys/systm.h>
 #include <sys/reboot.h>
+#include <sys/proc.h>
+#include <sys/signalvar.h>
 #include <sys/kernel.h>
 #include <sys/kthread.h>
 #include <sys/interrupt.h>
@@ -91,9 +93,27 @@
 shutdown_intr(void *arg __unused, void *frame __unused)
 {
 	kprintf("Caught SIGTERM from host system. Shutting down...\n");
-	if (initproc != NULL) {
+
+	/*
+	 * (1) ksignal(initproc, SIGUSR2) is a silent no-op until init(8)
+	 *     has installed its SIGUSR2 handler: issignal() drops
+	 *     default-action signals to pid <= 1 (kern_sig.c:2134) and
+	 *     execsigs() only resets caught signals, so a pre-exec or
+	 *     non-init(8) pid 1 can never take it.  Gate on p_sigcatch --
+	 *     the kernel's own SIG_CATCH/SIG_DFL delivery criterion.
+	 *
+	 * (2) reboot(RB_POWEROFF) resolved to the HOST libc reboot(2)
+	 *     (the kernel defines no symbol `reboot`; the vkernel links
+	 *     as an ordinary host program), asking the host to reboot or
+	 *     power off the machine it runs on.  shutdown_nice() takes
+	 *     the in-kernel path; RB_HALT|RB_POWEROFF rides in
+	 *     shutdown_howto into boot(), ending in shutdown_halt ->
+	 *     cpu_halt() (exit(EX_OK), monitor exits -- a clean stop).
+	 */
+	if (initproc != NULL &&
+	    SIGISMEMBER(initproc->p_sigacts->ps_sigcatch, SIGUSR2)) {
 		ksignal(initproc, SIGUSR2);
 	} else {
-		reboot(RB_POWEROFF);
+		shutdown_nice(RB_POWEROFF | RB_HALT);
 	}
 }

--- a/sys/kern/kern_shutdown.c
+++ b/sys/kern/kern_shutdown.c
@@ -202,11 +202,19 @@
 {
 	shutdown_howto = howto;
 	
-	/* Send a signal to init(8) and have it shutdown the world */
-	if (initproc != NULL) {
+	/*
+	 * Send a signal to init(8) and have it shutdown the world.
+	 * Only signal init if init can actually receive it: issignal()
+	 * drops default-action signals to pid <= 1 (kern_sig.c), so a
+	 * blind ksignal() before init(8) has armed its handlers (or for
+	 * a pid 1 that is not init(8)) is a silent no-op.  Fall back to
+	 * the in-kernel shutdown path in that case.
+	 */
+	if (initproc != NULL &&
+	    SIGISMEMBER(initproc->p_sigacts->ps_sigcatch, SIGINT)) {
 		ksignal(initproc, SIGINT);
 	} else {
-		/* No init(8) running, so simply reboot */
+		/* No init(8) able to take the signal: simply shut down */
 		boot(RB_NOSYNC);
 	}
 	return;
@@ -573,7 +581,14 @@
 	if (p == NULL)
 		return;
 	if ((fdp = p->p_fd) != NULL) {
-		kern_closefrom(0);
+		/*
+		 * kern_closefrom() operates on curproc; only valid here
+		 * when the process being cleaned IS the shutdown caller.
+		 * boot() can be entered from an ithread (e.g. the vkernel
+		 * shutdown_intr), where curproc is NULL.
+		 */
+		if (p == curproc)
+			kern_closefrom(0);
 		if (fdp->fd_cdir) {
 			cache_drop(&fdp->fd_ncdir);
 			vrele(fdp->fd_cdir);