DF-5437 / verdict.json
{ "finding_id": "DF-5437", "status": "reproduced", "reproduced": true, "impact": "dos", "confidence": "certain", "verdict": "Mechanism-level reproduction with production chain shape and constants: ulpt_open() runs ulpt_reset() when it observes sc_fflags==0 (ulpt.c:413-418), which issues two sequential synchronous EP0 control requests each with a 2*USB_MS_HZ (2000 ms) timeout (ulpt.c:180,183); the wait loop inside usbd_do_request_flags() is lksleep(xfer, xfer_lock, 0, \"WXFER\", hz) with NO PCATCH (usb_request.c:648-650), uninterruptible by any signal including SIGKILL. An evil printer that NAKs the control data/status stage stretches every open() to the full 2x2 s (plus the 1 s loop granularity). The harness implements the verbatim wait loop and the two-sequential-request structure with the production timeout; the child opener SIGKILLed at +100 ms was reaped only at 4.014/4.017/4.015 s across three runs (status=0x9: the pending SIGKILL is carried through the whole kernel sleep and lands only at syscall exit), dmesg showing ELAPSED=4000 ms x3. /dev/ulptN is 0644 (ulpt.c:628) so the trigger is a plain unprivileged open(); an open-loop pins nthreads each for ~4 s unkillable. With the fix.diff mechanism (per-request timeout bounded to USB_MS_HZ/4 = 250 ms, mirrored via hw.hx5437.timeout_ms=250) the same probe bounds the stall at 0.500-0.515 s (dmesg ELAPSED=500/501 ms). No usb-printer device model exists in guest QEMU 7.2 and X86_64_GENERIC lacks the USB_REQ_DEBUG fault-injection sysctls, hence the KLD-harness evidence class (DF-5383 precedent).", "exploit_chain": "evil USB printer (printer-class descriptors, BI alt setting) enumerates normally, then NAKs the data/status stage of both SOFT_RESET request forms; any unprivileged local users open(\"/dev/ulptN\", O_RDONLY) (world-readable 0644 node) blocks uninterruptibly ~4 s per open; a loop of opens from N processes pins N kernel threads in D-state, immune to SIGKILL, repeatable indefinitely (device-triggered local DoS amplification). Bounded per open; no memory corruption.", "evidence": "run.log: [R] SIGKILL sent at +100 ms; child reaped after 4.014 s (status=0x9) x3 baseline, then reaped after 0.515 s with the fix mechanism; dmesg tail: HX5437: open() complete: ELAPSED=4000 ms (req1=0 req2=0, timeout_ms=2000) x3 and ELAPSED=500/501 ms ... timeout_ms=250.", "kernel_refs": [ "sys/bus/u4b/serial/ulpt.c:177", "sys/bus/u4b/serial/ulpt.c:180", "sys/bus/u4b/serial/ulpt.c:183", "sys/bus/u4b/serial/ulpt.c:413", "sys/bus/u4b/serial/ulpt.c:628", "sys/bus/u4b/usb_request.c:557", "sys/bus/u4b/usb_request.c:648" ], "poc_changes": "Harness is first-iteration correct (verbatim wait-loop shape and production constants); run.sh adjusted to tolerate the fix-probes exit code (rc=1 when the stall drops below the 1 s oracle -- that is the fix working).", "attempts": 2, "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": 120, "guest_dirty": 0, "build_cmd": "sh build.sh (in-tree kmod /usr/src/sys/bus/u4b/serial/hx5437 + cc -O2 probe)", "run_cmd": "sh run.sh (kldload; 3x baseline probe at timeout_ms=2000; sysctl hw.hx5437.timeout_ms=250; 2x fix-mechanism probe; kldunload; dmesg tail)", "code_hash": "5827939525f383a4b204328b1b533ced1e13b3cb2c7512aa55def37382d14808", "notes": "The deep fix (PCATCH on the WXFER lksleep in usb_request.c so control-request waits are signal-interruptible) is framework scope and noted for upstream; fix.diff bounds the driver-side exposure (2x250 ms worst case) which is what the harness validated. Bounded stall per open; severity Low.", "recommended_fix": "Bound both ulpt_reset() SOFT_REQUEST timeouts to USB_MS_HZ/4 (fix.diff); optionally make the WXFER wait PCATCH-interruptible in usb_request.c.", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 1, "fix_verdict": "Baseline (production 2x2000 ms): opener survives SIGKILL 4.014/4.017/4.015 s, dmesg ELAPSED=4000 ms x3. Fix mechanism (USB_MS_HZ/4 per request per fix.diff): 0.515/0.500 s, dmesg ELAPSED=500/501 ms -- 8x reduction, stall bounded below the uninterruptibility oracle.", "fix_evidence": "run.log baseline and fix sections; dmesg HX5437: open() complete: ELAPSED=... lines." } |