⬢ DragonFlyBSD Kernel Audit
DF-5496 / verdict.json
← back to finding ↓ download raw
{
  "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)."
}