DF-0019 / df0019.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | /* * DF-0019 trigger โ switch to the bsd4 scheduler, set queue_checks=0, * then run CPU-bound workload to hit bsd4_chooseproc_locked_cache_coherent. * * The default DragonFlyBSD user scheduler is "dfly" (kern_usched.c:72), * so the bsd4 code path is dormant unless processes explicitly switch * via usched_set(USCHED_SET_SCHEDULER, "bsd4"). * * Must be run as root: (a) to write kern.usched_bsd4.queue_checks sysctl, * (b) usched_set requires SYSCAP_NOSCHED. * * Build: cc -O2 -o df0019 df0019.c * Run: ./df0019 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <signal.h> #include <errno.h> #include <sys/wait.h> #include <sys/types.h> #include <sys/syscall.h> /* usched_set(2) โ DragonFlyBSD syscall 481 */ #define DFLY_USCHED_SET 481 #define USCHED_SET_SCHEDULER 0 static volatile sig_atomic_t stop = 0; static void handler(int s) { (void)s; stop = 1; } static int dfly_usched_set(pid_t pid, int cmd, const char *name) { return syscall(DFLY_USCHED_SET, pid, cmd, name, (int)strlen(name)+1); } static void set_sysctl(const char *name, const char *val) { char cmd[256]; snprintf(cmd, sizeof(cmd), "sysctl %s=%s", name, val); system(cmd); } int main(int argc, char **argv) { int nkids = (argc > 1) ? atoi(argv[1]) : 24; int i; pid_t *kids; kids = calloc(nkids, sizeof(pid_t)); if (!kids) { perror("calloc"); return 2; } /* Step 1: switch THIS process to the bsd4 scheduler. * After this, all forked children inherit bsd4 scheduling. */ fprintf(stderr, "[*] switching pid %d to bsd4 scheduler...\n", (int)getpid()); if (dfly_usched_set(0, USCHED_SET_SCHEDULER, "bsd4") < 0) { fprintf(stderr, "[!] usched_set failed: %s\n", strerror(errno)); fprintf(stderr, "[!] cannot switch to bsd4 scheduler โ bug unreachable.\n"); return 3; } fprintf(stderr, "[+] now running under bsd4 scheduler.\n"); /* Step 2: set queue_checks=0 (root sysctl). * The sysctl write is global and affects all bsd4-scheduled processes. */ fprintf(stderr, "[*] setting kern.usched_bsd4.queue_checks=0...\n"); set_sysctl("kern.usched_bsd4.queue_checks", "0"); /* Step 3: fork CPU-bound children to populate the bsd4 runqueues and * force the scheduler through bsd4_release_curproc -> * bsd4_select_curproc -> bsd4_chooseproc_locked_cache_coherent. * With queue_checks=0 the while loop is skipped, min_level_lwp stays * NULL, and KASSERT(lp) at usched_bsd4.c:1548 fires -> panic. */ fprintf(stderr, "[*] forking %d CPU-bound bsd4 kids...\n", nkids); signal(SIGALRM, handler); for (i = 0; i < nkids; i++) { pid_t p = fork(); if (p == 0) { /* child: tight CPU spin with short sleeps to force * acquire/release cycles through the scheduler */ alarm(30); while (!stop) { volatile unsigned long x = 0; for (volatile int j = 0; j < 50000; j++) x += j; usleep(50); /* blocking syscall -> lwkt_passive_release */ } _exit(0); } kids[i] = p; } /* parent also spins */ alarm(20); while (!stop) { volatile unsigned long x = 0; for (volatile int j = 0; j < 500000; j++) x += j; } /* If we survive, clean up. On a fixed kernel (queue_checks clamped or * sysctl rejected) we should reach here cleanly. */ fprintf(stderr, "[*] parent survived, 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); } free(kids); fprintf(stderr, "[*] survived โ no panic (FIXED kernel or path not hit). exit 0.\n"); return 0; } |