usb_detach() races asynchronous usb_bus_attach() — NULL-softc guard bypassed, wild-pointer write via dev->softc (observed -8) in usb_callout_drain GUEST : DragonFly 6.5-DEVELOPMENT x86_64 (X86_64_GENERIC), QEMU/KVM 6 vCPU, -device qemu-xhci,id=x0, xhci.ko modular (not in GENERIC) TRIGGER : root# kldload xhci; kldunload xhci (zero delay — one cycle is enough) RESULT : kernel panic (trap 0xc, supervisor WRITE at VA 0x498), crash dump, reboot. Reproduced 4/4 zero-delay attempts. == Exact crash (crash dump vmcore.0, kgdb) == panic: page fault Trace beginning at frame 0xfffff8011846d4a8 trap_fatal() at trap_fatal+0x301 trap_pfault() at trap_pfault+0x431 trap() at trap+0x17c calltrap() at calltrap+0x9 --- trap 000000000000000c, rip = ffffffff8068734a, rsp = ..., rbp = ... --- _callout_cancel_or_stop() at _callout_cancel_or_stop+0x1a usb_detach() at usb_detach+0x3c boot() called on cpu#2 -> dump to /dev/vbd0s1b trap frame (decoded from stack, frame at 0xfffff8011846d5e8): tf_rdi = 0x488 -> &bus->power_wdog tf_rbx = 0xfffffffffffffff8 (-8) -> usb_detach's `bus` tf_addr = 0x498 -> fault VA = rdi + 0x10 tf_err = 0x2 -> supervisor WRITE tf_rip = _callout_cancel_or_stop+0x1a: lock andl $0xffffefff,0x10(%rdi) usb_detach disassembly (kern.0): callq device_get_softc test %rax,%rax ; je early_return <- usb_controller.c:195 NULL check mov %rax,%rbx <- bus = -8 (wild, non-NULL) lea 0x490(%rax),%rdi <- &bus->power_wdog = -8+0x490 = 0x488 callq callout_drain <- usb_controller.c:200 -> fault == Concurrency proof == The panic message is character-interleaved on the serial console with usbus0: 5.0Gbps Super Speed USB v3.0 which is usb_bus_attach()'s device_printf at usb_controller.c:736 — the explore kthread was INSIDE usb_bus_attach (before the device_set_softc at :791) on another CPU at the moment usb_detach faulted. Dump cross-check: the usbus device object (0xfffff80117958500, ivars == bus 0xfffff801188e0528, matching the intrhook warning "usb: func=... arg=0xfffff801188e0528") has its softc field equal to `bus` at dump time — the async :791 device_set_softc() had landed in the same device object the detach path is tearing down. == Why the race is nearly deterministic on DragonFly == usb_attach_sub() calls config_intrhook_establish() at runtime (usb_controller.c:871). On DragonFly run_interrupt_driven_config_hooks() then sleeps ~10s in "conifhk" ("**WARNING** waiting for the following device to finish configuring ... Giving up") before returning, so the attach_msg is queued only ~10s into kldload; when kldload() returns, usb_bus_attach() has only just started in the explore kthread. An immediately following kldunload (devclass_delete_driver -> xhci_pci_detach -> device_delete_children -> usb_detach) therefore always lands inside the async window.