DF-5423 / verdict.json
{ "finding_id": "DF-5423", "status": "reproduced", "reproduced": true, "impact": "dos", "confidence": "certain", "verdict": "Root-hub reset never returns and wedges the USB subsystem until reboot, reproduced 3x through the uftdi rig (twice with an open writing tty, once with the port closed and the device merely attached - the open tty is NOT required). usbconfig -d ugen0.1 reset puts the process in D4 on USBSLP; kill -9 cannot remove it; subsequent usbconfig list hangs (bus explore path dead); only a reboot clears it. Root cause: the generic-usb ioctl tail at usb_dev.c:1139-1149 is an unbounded uninterruptible spin-wait on re_enumerate_wait, and the root hubs flag is never serviced because uhub_explore_handle_re_enumerate() (the only clear sites, usb_hub.c:474/498/511/515) is invoked only for hub children (usb_hub.c:550); the root hub has parent_hub == NULL. Matches the D4 hang the umodem.c pass-2 observed on this family.", "exploit_chain": "root runs usbconfig -d ugen0.1 reset (ugen node 0600) with any device on the root hub -> unkillable D4 process + permanent whole-bus USB outage (hotplug/enumeration/userland usb ioctls) until reboot. No memory-safety primitive.", "evidence": "run.log: 1075 D4 USBSLP usbconfig -d ugen0.1 reset, kill -9 reported Killed yet the ps line persists, and timeout 10 usbconfig list produced no output (wedged); two further reproductions earlier in the session (964 D1 USBSLP surviving kill -9; T4 with 1MiB writer in flight). dmesg during the cycles: ugen0.2 disconnected -> device still open, forcing close -> detached -> re-attach (root-hub power-mode churn), guest otherwise alive.", "kernel_refs": [ "sys/bus/u4b/usb_dev.c:1137-1149", "sys/bus/u4b/usb_generic.c:1801-1802", "sys/bus/u4b/usb_hub.c:550", "sys/bus/u4b/usb_hub.c:461-467", "sys/bus/u4b/usb_hub.c:474", "sys/bus/u4b/usb_util.c:134" ], "poc_changes": "No seed PoC existed; trigger is a one-line admin command plus ps/kill probes (run.sh).", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 900, "guest_dirty": 0, "build_cmd": "(none - stock usbconfig)", "run_cmd": "usbconfig -d ugen0.1 reset; ps -ax -o pid,stat,wchan,command | grep usbconfig; kill -9 <pid>; usbconfig list (see run.sh)", "code_hash": "378b15184959f4c640a3eb434920c1ca7afa9d616af9d8bdd5df8eea329a4645", "notes": "Reproduced identically with the ucom tty open+writing and with it closed - precondition is just a child device attached to the root hub being reset. The guest survives otherwise (ssh, uptime fine). Same wait loop is latent-dangerous for non-root devices whenever the explore thread is stalled (e.g. by DF-5422s close hang), making the unbounded loop worth bounding regardless of trigger privilege.", "recommended_fix": "Bound the re-enumeration wait loop in usb_dev.c:1139-1149 (~2 s of hz/128 sleeps -> ETIMEDOUT) and/or service the root hubs own re_enumerate_wait from the bus explore thread (full diff in fix.diff).", "fix_status": "not_testable", "fix_baseline_reproduced": 1, "fix_patched_reproduced": null, "fix_verdict": "fix.diff authored against read-only sys/ (usb_dev.c), git apply --check passes. Not boot-tested (full kernel rebuild out of scope for this pack); adding a loop bound cannot lengthen the wait - mechanism trivial by inspection.", "fix_evidence": "fix.diff; run.log baseline (D4/USBSLP, kill -9 immune, usbconfig list wedge)" } |