DF-5496 / verdict.json
{ "finding_id": "DF-5496", "status": "reproduced", "reproduced": true, "impact": "dos", "confidence": "certain", "verdict": "REPRODUCED on the guest (KLD-harness-with-production-code class; no MCT USB232/Sitecom/Belkin F5U109/F5U409 device model exists): every umct cfg-callback step is a synchronous EP0 vendor write via ucom_cfg_do_request with a 1000 ms timeout (umct.c:332-333) whose usb_error_t is discarded at the only site (umct.c:334-337, (ignored)), so no chain aborts on device failure. Pass-2 accounting precision: the first-open cfg_param task is queued before ucom_cfg_open (usb_serial.c:842 vs :865) and bails at :1626-1628 (!LL_READY) -- first-open param is a no-op for every ucom driver -- and umct registers no ucom_cfg_open (:777 pause skipped), so first open issues only the coalesced line-state chain DTR+RTS+BREAK-off = 3 x 1000 ms (umct.c:413,:426,:400), plus the HUPCL DTR drop at close (usb_serial.c:694-696 -> umct.c:413, 1 x 1000 ms); measured closer block 3885 ms. Every later tcsetattr re-queues the param task (double-buffered, usb_serial.c:1691-1693): SET_BAUD(umct.c:472) + SET_LCR(umct.c:503) + hz/10 core pause (:1636) = ~2.1 s -- IDENTICAL for B0 and any rate (no B0 branch in umct_cfg_param; umct_pre_param accepts everything, umct.c:459-463); measured closer block 5952 ms (one tcsetattr) and 8049 ms (burst of 5, coalesced to 2 full param executions by the msignal double buffer, usb_process.c:259-331 -- a paced/sustained loop extends the block without bound because usb_proc_mwaits uninterruptible cv_wait drops sc_lock while asleep, usb_process.c:364-393) -- uninterruptible per usb_serial.c:671-672+953-957; umct registers no ucom_cfg_close so the cfg_close task itself is a 0-request no-op. Unplug mid-cycle pins the USB hub event thread identically via ucom_detach->usb_proc_drain (usb_serial.c:394). Each simulated request is the real in-kernel cost of an unanswered EP0 data/status stage (lksleep WXFER 1000 ms, usb_request.c:643/700-704) on the real usb_process taskqueue, per-request unique wait channel. Fix mechanisms validated at runtime: abort-on-first-error 3033 ms (B), abort+250 ms bound 834 ms (C); patched production umct.ko compiles 8/8 hunks clean.", "exploit_chain": "evil adapter (MCT USB232 0711:0210, Sitecom USB232 6189:2068, D-Link DU-H3SP, Belkin F5U109/F5U409 050d:010a/0129) inserted; it never answers EP0 vendor data/status stages (or answers at just-under-timeout, making every request succeed with no error anywhere); first open(2) of /dev/ttyUx occupies the adapters ucom taskqueue ~3.0 s uninterruptibly (+1.0 s HUPCL at close); every tcsetattr adds ~2.1 s while a session holds the tty open (user-amplifiable: sustained loop keeps the tq busy 2.1 s per instance indefinitely; burst ceiling 2 queued instances); close(2) blocks SIGKILL-immune behind the queue (measured 5.95 s / 8.05 s); pulling the device mid-cycle stalls the USB hub event thread up to the same duration per cycle.", "evidence": "run.log ELAPSED lines: UM5496[A]=3885 ms, [2]=5952 ms, [3]=8049 ms (5-burst), [B]=3033 ms (fix abort), [C]=834 ms (fix abort+250ms), plus absolute per-request START timestamps showing every request sleeping its full ~980 ms (e.g. phase A: 57/1038/2028/3049/4058 ms) and the phase-A3 coalescing instrumentation (burst of 5 -> 2 param-task executions); build.log (-Werror-clean in-tree kmod build); fix_compile.log (8/8 hunks, patched production umct.ko, zero compiler diagnostics -- only the literal -Werror flag matches error).", "kernel_refs": [ "sys/bus/u4b/serial/umct.c:314-339", "sys/bus/u4b/serial/umct.c:332-333", "sys/bus/u4b/serial/umct.c:334-337", "sys/bus/u4b/serial/umct.c:390-401", "sys/bus/u4b/serial/umct.c:400", "sys/bus/u4b/serial/umct.c:404-414", "sys/bus/u4b/serial/umct.c:413", "sys/bus/u4b/serial/umct.c:416-427", "sys/bus/u4b/serial/umct.c:426", "sys/bus/u4b/serial/umct.c:459-463", "sys/bus/u4b/serial/umct.c:465-504", "sys/bus/u4b/serial/umct.c:472", "sys/bus/u4b/serial/umct.c:503", "sys/bus/u4b/serial/usb_serial.c:322", "sys/bus/u4b/serial/usb_serial.c:671-672", "sys/bus/u4b/serial/usb_serial.c:694-696", "sys/bus/u4b/serial/usb_serial.c:771-778", "sys/bus/u4b/serial/usb_serial.c:801-903", "sys/bus/u4b/serial/usb_serial.c:842", "sys/bus/u4b/serial/usb_serial.c:865", "sys/bus/u4b/serial/usb_serial.c:874-880", "sys/bus/u4b/serial/usb_serial.c:939-957", "sys/bus/u4b/serial/usb_serial.c:394", "sys/bus/u4b/serial/usb_serial.c:1357-1420", "sys/bus/u4b/serial/usb_serial.c:1620-1637", "sys/bus/u4b/serial/usb_serial.c:1691-1693", "sys/bus/u4b/serial/usb_serial.h:220-221", "sys/bus/u4b/usb_request.c:561", "sys/bus/u4b/usb_request.c:643", "sys/bus/u4b/usb_request.c:700-704", "sys/bus/u4b/usb_process.c:259-331", "sys/bus/u4b/usb_process.c:364-393" ], "poc_changes": "Seed concept executed as the DF-5476 ubsa family rig adapted to umcts exact production shapes: 3-request first-open line-state chain, 2-request tcsetattr chain (identical for B0 -- umct has no B0 branch, unlike ubsas 7-request B0 variant), 1-request HUPCL close drop modeled as a dedicated tq task queued by the closer (production ucom_shutdown ordering), 0-request open/status/close tasks, the production LL_READY gate that makes the first-open param task bail (usb_serial.c:1626-1628), absent core open-pause (no callback), production 1000 ms timeout (umct.c:333), real usb_proc machinery, closer mirroring ucom_closes synchronous usb_proc_mwait entered ~150 ms into the chain, and the DF-5476 v2 fidelity lesson applied from the start (unique per-request wait channels). New this instance: an amplification phase (5-burst) instrumenting the production msignal double-buffer coalescing ceiling (2 executions), and the first fix.diff draft used != USB_ERR_OK which does not exist in this trees usb.h -- caught by the on-guest compile (build_failed -> corrected to != 0 per family precedent, rebuilt clean).", "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": 30, "guest_dirty": 1, "build_cmd": "sh build.sh (cd /usr/src/sys/bus/u4b/serial/um5496 && make; artifacts: umct5496stall.ko)", "run_cmd": "sh run.sh (dmesg -c; kldload /root/df5496/umct5496stall.ko; kldunload umct5496stall; dmesg | grep UM5496 | tee /root/df5496/run.log)", "code_hash": "8b3b58a457a0184f3db8bad8cf03879012241ad712088f53a3b3e38223907258", "notes": "Low severity per family precedent (evil-USB precondition, seconds-scale, cf. DF-5421 uchcom / DF-5424 uftdi / DF-5425 uslcom / DF-5449 uvscom / DF-5456 umoscom / DF-5476 ubsa / DF-5487 uvisor). umcts per-tcsetattr chain (2.1 s) is the lightest in the family, but there is NO B0 variant -- every rate costs the same, and the first-open cycle (3.0 s) plus HUPCL close (1.0 s) are family-typical. Negative results this pass: umct issues ZERO control-IN reads anywhere (UMCT_GET_MSR/UMCT_GET_LCR at umct.c:72-75 are dead constants -- the DF-1062/2342/5448/5455 uninit-read class is structurally absent); umct_intr_callback_sub has the CORRECT actlen<2 gate before consuming its 2-byte status payload (umct.c:353-358) unlike ulpt DF-5438; no synchronous EP0 from the USB callback thread (umcs DF-5382 class absent); attach/detach error paths safe (ucom_detach no-ops unless ATTACHED, usb_serial.c:371-372; ucom_attach self-cleans :344-348; no uether DF-5334 goto-detach class); the umct-specific endpoint-swap heuristic (umct.c:269-279, wMaxPacketSize==2) is memory-safe (whole-xfer pointer swap, per-endpoint DMA buffers stay correctly sized, sc_swap_cb dispatch guard written before ucom_attach); unsupported baud rates silently program divisor 0 (functional wrong-baud, upstream-inherited, no security impact -- noted, not filed). Guest reset (with-src) after the run.", "recommended_fix": "Bound the cfg-path control timeout (UMCT_CFG_TIMEOUT 250 ms) and abort the umct_cfg_param()/set_* chains on the first failed request via an error-returning umct_cfg_do_request() (full diff in fix.diff).", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 1, "fix_verdict": "Mechanism-level validation per family standard: harness phase B (abort-on-first-error) cuts the measured closer block from 5952 ms to 3033 ms and phase C (abort + 250 ms bound) to 834 ms; the 8-hunk fix.diff (error-returning umct_cfg_do_request, chain aborts in set_break/set_dtr/set_rts/cfg_param, bounded UMCT_CFG_TIMEOUT) applies 8/8 clean (git apply --check on the audit tree, patch -p1 on the guest) and compiles the patched production umct.ko with zero compiler warnings on the guest (fix_compile.log). A full patched-kernel boot is not required to observe a per-request timeout bound + early-abort behavior the harness exercises directly.", "fix_evidence": "fix.diff (sha256 b54552212a69cf7decff27f5f8506a0407726ae5bd166a3d9b110e2a00b92f11); fix_compile.log (patched production umct.ko, zero compiler diagnostics; the only error string is the literal -Werror flag); run.log phases B/C ELAPSED lines (3033 ms / 834 ms vs baseline 5952 ms)." } |