{
  "finding_id": "DF-5394",
  "status": "reproduced",
  "reproduced": true,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "usb_detach() (usb_controller.c:188) uses device_get_softc()==NULL (:191,:195) as its only guard, but the softc is only set asynchronously by usb_bus_attach() at :791 in the explore kthread; a controller detach landing inside that window (kldunload/PCI hot-unplug/HCD attach failure) either skips teardown entirely while the HCD softc and the embedded struct usb_bus are freed under 4 live USB kthreads and a queued attach_msg, or \u2014 observed deterministically 5x, 2 decoded crash dumps with identical signature \u2014 reads racing heap garbage from the device object (bus=0xfffffffffffffff8), passes the NULL guard, and executes usb_callout_drain(&bus->power_wdog) at :200, faulting a supervisor WRITE at 0x498 = (-8)+offsetof(power_wdog)+0x10 -> panic + dump. The panic text char-interleaves with usb_bus_attachs :736 printf, proving the cross-CPU concurrency; the dump shows the async :791 device_set_softc landing in the device object being torn down. DragonFlys runtime config_intrhook machinery (~10s conifhk stall in usb_attach_sub before attach_msg is queued) plus a slow console make the race land on the first kldload/kldunload pair. Root-triggered local kernel memory corruption / reliable DoS; not unprivileged-reachable. fix.diff (set softc synchronously in usb_attach_sub before signalling attach_msg + move the power_wdog drain after the detach job) validated: stock kernel panics on pair 1, patched kernel survives 32/32 pairs with zero leaked kthreads and clean usbus0 detach lines every cycle.",
  "exploit_chain": "root (or physical hot-unplug): kldload xhci; kldunload xhci -> devclass_delete_driver -> xhci_pci_detach -> device_delete_children -> usb_detach races the explore kthread executing usb_bus_attach -> wild softc (observed -8) passes the NULL guard -> supervisor write fault at 0x498 via &bus->power_wdog -> kernel panic (dump+reboot). Teardown-skip variant: usb_detach early-returns -> HCD frees struct usb_bus under the live explore/callback kthreads -> use-after-free of the whole bus (kthreads, armed 4s power_wdog callout, usbpf ifp, queued attach_msg). No uid=0 chain developed: the corrupted write is a near-NULL fixed-offset fault (DoS), not a controlled primitive; trigger requires root/physical.",
  "evidence": "panic.txt (decoded trap frames of BOTH dumps: tf_rbx=-8, tf_rdi=0x488, tf_addr=0x498, tf_err=0x2 supervisor write, tf_rip=_callout_cancel_or_stop+0x1a); kgdb.txt (backtrace usb_detach+0x3c <- device_delete_children <- xhci_pci_detach, usb_detach disassembly showing the guard and the 0x490 lea, and the usbus device object whose softc==bus via the racing :791 write); serial-run5-panic.log (clean panic header, crash dump #2, stock snapshot kernel, first zero-delay pair); boot.log.df5394-run2-panic (dump #1 console with the char-interleaved :736 printf = concurrency proof); boot.log.df5394-run1-ddb (first crash); race.sh (guest trigger); run-fixed.log (patched kernel: 32/32 pairs clean, 0 leaked kthreads); fix.diff (validated)",
  "kernel_refs": [
    "sys/bus/u4b/controller/usb_controller.c:188-243",
    "sys/bus/u4b/controller/usb_controller.c:191",
    "sys/bus/u4b/controller/usb_controller.c:195-198",
    "sys/bus/u4b/controller/usb_controller.c:200",
    "sys/bus/u4b/controller/usb_controller.c:736",
    "sys/bus/u4b/controller/usb_controller.c:790-794",
    "sys/bus/u4b/controller/usb_controller.c:810-907",
    "sys/bus/u4b/controller/usb_controller.c:871-874",
    "sys/bus/u4b/controller/usb_controller.c:897-906",
    "sys/bus/u4b/controller/usb_controller.c:419-451",
    "sys/bus/u4b/controller/xhci_pci.c:269-343",
    "sys/bus/u4b/controller/uhci_pci.c:353-365",
    "sys/bus/u4b/usb_process.c:79-181",
    "sys/kern/subr_bus.c:853-892",
    "sys/kern/subr_autoconf.c (run_interrupt_driven_config_hooks)"
  ],
  "poc_changes": "No compileable seed existed; authored race.sh (guest-side kldload/kldunload cycler) and the host-side procedure. Key discovery during verification: with the default file-backed serial console the attacher usually wins the race (40 clean cycles); routing the console to a slow unix-socket serial (df5394-serdrv.py) throttles usb_bus_attachs device_printfs and makes the crash land on the first pair. Panic-to-dump armed via debug.debugger_on_panic=0 so crashes reboot instead of sitting in DDB.",
  "attempts": 6,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (stock, baseline) / #1: Sun Oct 4 01:09:43 UTC 2026 (fix build)",
  "runtime_sec": 6600,
  "guest_dirty": 0,
  "build_cmd": "cd /usr/src && patch -p1 < /tmp/fix.diff && make -j6 nativekernel KERNCONF=X86_64_GENERIC && make installkernel   (fix validation only; the bug itself needs no build)",
  "run_cmd": "vm.sh run_root sysctl debug.debugger_on_panic=0 >/dev/null; kldunload xhci; kldload xhci; kldunload xhci   (guest booted with QEMU_EXTRA=-device qemu-xhci,id=x0 -serial unix:df5394-ser.sock,server,nowait)",
  "code_hash": "sha256:a84f6118fb05d5c9f254469e1a62ed14d5071b91d68833c885a3e485acc6f877",
  "notes": "Two failure modes of the same root cause: (1) teardown-skip (softc read NULL -> usb_detach frees nothing -> usb_bus freed under live kthreads; observable as leaked usbus kthreads when it does not fault); (2) wild-softc (device object mutated by the racing attacher -> garbage non-NULL softc passes the guard -> :200 drain faults; the observed garbage was -8 in both dumps). The 5 crashes: run1 DDB stop, run2 dump#1, run3 DDB (no dump), run5 dump#2 on the clean snapshot kernel (first pair). DragonFly-specific aggravator: runtime config_intrhook_establish in usb_attach_sub stalls ~10s in conifhk (subr_autoconf.c: ran hooks stay listed until disestablished) \u2014 worth an upstream look but out of this files scope. Guest left clean: vm.sh reset with-src performed after validation.",
  "recommended_fix": "Set the usbus softc synchronously in usb_attach_subs success branch (before signalling attach_msg) so usb_detach always performs the full teardown dance ordered by the explore-proc FIFO, and move usb_callout_drain(&bus->power_wdog) in usb_detach to after the detach-job mwait (usb_bus_attach arms the callout as its last action).",
  "fix_status": "fixed",
  "fix_baseline_reproduced": true,
  "fix_patched_reproduced": false,
  "fix_verdict": "Baseline on the identical snapshot/kernel-config: panic (trap 0xc, usb_detach+0x3c, dump extracted) on the first zero-delay kldload/kldunload pair with the socket serial console. Patched kernel (fix.diff via make nativekernel + installkernel): identical trigger, 12 zero-delay pairs + 20 more = 32/32 clean, usbus0: detached on every cycle (dmesg count 13), zero leaked usbus kthreads, guest healthy, boot attach unaffected (uhub0 attached normally).",
  "fix_evidence": "run-fixed.log (full matrix + serial tail); serial-run5-panic.log (baseline panic on pair 1); /var/crash/vmcore.0 of the baseline crash; fix.diff"
}