⬢ DragonFlyBSD Kernel Audit
DF-5773 / storm_demo.c
← back to finding ↓ download raw
/*
 * DF-5773 mechanism harness -- a faithful, self-contained extraction of
 * sys/platform/vkernel64/platform/init.c's monitor/child structure:
 *
 *   init.c:204   while ((childpid = fork()) != 0) {
 *   init.c:206-211   SIG_IGN for INT/QUIT/HUP
 *   init.c:217-218   sa.sa_handler = handle_term; sigaction(SIGTERM,...)
 *   init.c:224-227   waitpid() ...
 *   init.c:228       if (WEXITSTATUS(status) != EX_VKERNEL_REBOOT) return 0;
 *   init.c:500-506   handle_term: kill(childpid, sig)
 *
 * First-generation children inherit SIG_DFL for SIGTERM (the sigaction
 * calls execute in the monitor AFTER fork).  After the child exits 32
 * (cpu_reset), the monitor reforks -- and THIS child inherits
 * handle_term with childpid == 0, so a SIGTERM delivered during its
 * "boot window" executes kill(0, SIGTERM): the whole process group,
 * including itself (re-armed on handler exit: a self-sustaining storm)
 * and every bystander in the pgrp.
 *
 * usage: storm_demo <window_seconds>
 * Observe from a runner with a sleeper sibling in the same pgrp.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <sys/wait.h>

#define EX_VKERNEL_REBOOT 32

static pid_t childpid;

static void handle_term(int sig) { kill(childpid, sig); }	/* init.c:505 */
static void pause_ms(int sec);

int
main(int ac, char **av)
{
	struct sigaction sa;
	int window = ac > 1 ? atoi(av[1]) : 5;
	int status;

	while ((childpid = fork()) != 0) {
		memset(&sa, 0, sizeof(sa));
		sigemptyset(&sa.sa_mask);
		sa.sa_handler = SIG_IGN;
		sigaction(SIGINT, &sa, NULL);
		sigaction(SIGQUIT, &sa, NULL);
		sigaction(SIGHUP, &sa, NULL);

		sa.sa_handler = handle_term;
		sigaction(SIGTERM, &sa, NULL);

		while (waitpid(childpid, &status, 0) != childpid) {
			/* ECHILD only; keep waiting otherwise (init.c:224-227) */
		}
		if (WEXITSTATUS(status) != EX_VKERNEL_REBOOT)
			return 0;
		/* refork: the reboot cycle (init.c:228-230) */
	}

	/* child: "boot" -- the pre-initshutdown window */
	pause_ms(window);
	_exit(EX_VKERNEL_REBOOT);	/* cpu_reset() */
	return 0;
}

static void pause_ms(int sec)
{
	struct timespec ts = { sec, 0 };
	nanosleep(&ts, NULL);
}