⬢ DragonFlyBSD Kernel Audit
DF-5540 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-5540",
  "status": "reproduced",
  "reproduced": true,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "REPRODUCED on the guest (KLD-harness-with-production-code class; no Arkmicro ARK3116 USB device model exists): every uark cfg-callback step is a synchronous EP0 vendor write via ucom_cfg_do_request with a 1000 ms timeout (uark.c:429-430) whose usb_error_t is discarded at the only site (uark.c:431-434, (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 uark registers no ucom_cfg_open (:771-778 pause skipped), so first open issues only the coalesced line-state chain whose callback mask contains ONLY break (uark.c:140 registers set_break; set_dtr/set_rts/set_ring NULL, usb_serial.c:1374-1381) = 1 x 1000 ms (uark.c:414); measured closer block 921 ms. Every later tcsetattr re-queues the param task (double-buffered, usb_serial.c:1691-1693): uark_cfg_param chains SIX writes (uark.c:361 reg3=0x83, :362 div LSB, :363 div MSB, :364 reg3=0x03, :394 reg3=0x00, :395 reg3=LCR) + hz/10 core pause (:1636) = ~6.1 s -- IDENTICAL for every accepted rate 300..115200 (uark_pre_param uark.c:343 rejects B0/0 and >115200; there is no B0 branch) -- the heaviest single-rate chain in the audited family short of umcs 28-request open/close pair (DF-5383) and 3x umcts (DF-5496); measured closer block 6995 ms (one tcsetattr) and 13012 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+955-957; the closer owns 0 requests of its own (HUPCL DTR drop masked by the absent set_dtr, usb_serial.c:694-696 + :1374-1375; no ucom_cfg_close registered). TIOCSBRK/TIOCCBRK cost 1 x 1000 ms per toggle (uark.c:407-415). 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 2029 ms (B), abort+250 ms bound 561 ms (C); patched production uark.ko compiles 6/6 hunks clean.",
  "exploit_chain": "evil adapter (Arkmicro ARK3116 6547:0232) inserted, or any uark device whose EP0 goes silent mid-operation; 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 ~1.0 s uninterruptibly; every tcsetattr adds ~6.1 s while a session holds the tty open (user-amplifiable: sustained loop keeps the tq busy 6.1 s per instance indefinitely; burst ceiling 2 queued instances); TIOCSBRK/TIOCCBRK add 1.0 s per toggle; close(2) blocks SIGKILL-immune behind the queue (measured 6.995 s / 13.012 s); pulling the device mid-cycle stalls the USB hub event thread up to the same duration per cycle.",
  "evidence": "run.log ELAPSED lines: UA5540[A]=921 ms, [2]=6995 ms, [3]=13012 ms (5-burst), [B]=2029 ms (fix abort), [C]=561 ms (fix abort+250ms), plus absolute per-request START timestamps showing every request sleeping its full ~980-1000 ms (e.g. phase A2: 1088/2070/3061/4061/5061/6050 ms) and the phase-A3 coalescing instrumentation (burst of 5 -> 2 param-task executions); run.2.log stability re-run (854/6950/12963/2034/564 ms); build.log (-Werror-clean in-tree kmod build); fix_compile.log (6/6 hunks, patched production uark.ko, zero compiler diagnostics -- only the literal -Werror flag matches error).",
  "kernel_refs": [
    "sys/bus/u4b/serial/uark.c:138-148",
    "sys/bus/u4b/serial/uark.c:140",
    "sys/bus/u4b/serial/uark.c:343",
    "sys/bus/u4b/serial/uark.c:348-396",
    "sys/bus/u4b/serial/uark.c:361-364",
    "sys/bus/u4b/serial/uark.c:394-395",
    "sys/bus/u4b/serial/uark.c:398-405",
    "sys/bus/u4b/serial/uark.c:407-415",
    "sys/bus/u4b/serial/uark.c:414",
    "sys/bus/u4b/serial/uark.c:417-435",
    "sys/bus/u4b/serial/uark.c:429-430",
    "sys/bus/u4b/serial/uark.c:431-434",
    "sys/bus/u4b/serial/usb_serial.h:220-221",
    "sys/bus/u4b/serial/usb_serial.c:322",
    "sys/bus/u4b/serial/usb_serial.c:394",
    "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:757-779",
    "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:1357-1420",
    "sys/bus/u4b/serial/usb_serial.c:1374-1381",
    "sys/bus/u4b/serial/usb_serial.c:1619-1637",
    "sys/bus/u4b/serial/usb_serial.c:1626-1628",
    "sys/bus/u4b/serial/usb_serial.c:1636",
    "sys/bus/u4b/serial/usb_serial.c:1691-1693",
    "sys/bus/u4b/usb_request.c:643",
    "sys/bus/u4b/usb_request.c:700-704",
    "sys/bus/u4b/usb_request.c:759-790",
    "sys/bus/u4b/usb_process.c:259-331",
    "sys/bus/u4b/usb_process.c:364-393"
  ],
  "poc_changes": "Seed concept executed as the DF-5496 umct family rig adapted to uarks exact production shapes: 1-request first-open line-state chain (only set_break registered among the line callbacks -- the mask at usb_serial.c:1374-1381 drops DTR/RTS/ring), 6-request tcsetattr chain at ANY accepted rate (B0 rejected in pre_param -- no umct-style no-B0-branch note needed beyond identity across rates), 0-request HUPCL close (set_dtr NULL -> masked, unlike umcts 1-request drop), 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 (uark.c:429-430), real usb_proc machinery, closer mirroring ucom_closes synchronous usb_proc_mwait entered ~100-150 ms into the chain, and the DF-5476 v2 fidelity lesson applied from the start (unique per-request wait channels). One build iteration: v1 had a parenthesized-nested-ternary typo that failed the on-guest -Werror build (build_failed) -- replaced with a static request-name table, 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": 26,
  "guest_dirty": 1,
  "build_cmd": "sh build.sh  (cd /usr/src/sys/bus/u4b/serial/ua5540 && make; artifacts: uark5540stall.ko)",
  "run_cmd": "sh run.sh    (dmesg -c; kldload /root/df5540/uark5540stall.ko; kldunload uark5540stall; dmesg | grep UA5540 | tee /root/df5540/run.log)",
  "code_hash": "94f032760ea58f9cc64f40ac3540cdb15e60e665bcf52f088502f4fc2e169a83",
  "notes": "Low severity per family precedent (evil-USB precondition or seconds-scale local stall, cf. DF-5421 uchcom / DF-5424 uftdi / DF-5425 uslcom / DF-5449 uvscom / DF-5456 umoscom / DF-5476 ubsa / DF-5487 uvisor / DF-5496 umct / DF-5504 ucycom). uarks per-tcsetattr chain (6.1 s) is the heaviest single-rate chain in the audited family short of umcs 28-request open/close (DF-5383) and 3x umcts; the closer itself owns 0 requests (no set_dtr -> HUPCL masked, no cfg_close callback). Negative results this pass: uark issues ZERO control-IN reads anywhere (uark_cfg_write is write-only, uark.c:423-427 -- the DF-1062/2342/5448/5455 uninit-read class is structurally absent); divisor math safe (uark.c:343+359: speed in [300,115200] -> divisor in [26,10000], fits uint16; B0 rejected in pre_param -- no uvscom DF-5447 inversion class); bulk buffer math bounded (write sends only ucom_get_datas actlen <= 1024, uark.c:260-263; read frame len = max_len, ucom_put_data clamps, uark.c:290-294; uint32_t-vs-int actlen pun at uark.c:253 benign, <=1024); sc_msr/sc_lsr never written (uark.c:91-92 read at :403-404) -- constant-zero modem status, functional only, softc zeroed (no uninit leak, no ulpt DF-5438 short-packet class); attach/detach error paths safe (ucom_detach no-ops unless ATTACHED usb_serial.c:371-372, ucom_attach self-cleans :344-348; teardown order uark.c:241-243 family-correct; DF-5320 unplug-UAF is a core usb_serial defect already filed, no per-driver delta; DF-5394 attach-race is core usb_bus, N/A per-driver); no synchronous EP0 from the bulk callback threads (umcs DF-5382 class absent); error re-submission only clears the devices own pipe stall (uark.c:267-272,298-303); open/close/poll methods family-canonical (uark.c:437-442). Guest /usr/src uark.c reverted post-fix-compile; guest reset (with-src) after the run.",
  "recommended_fix": "Bound the cfg-path control timeout (UARK_CFG_TIMEOUT 250 ms) and abort the uark_cfg_param() 6-write chain on the first failed request via an error-returning uark_cfg_write() (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 6995 ms to 2029 ms and phase C (abort + 250 ms bound) to 561 ms; the 6-hunk fix.diff (error-returning uark_cfg_write with UARK_CFG_TIMEOUT, chain aborts across the 6-write uark_cfg_param chain) applies 6/6 clean (git apply --check on the audit tree, patch -p1 on the guest) and compiles the patched production uark.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 b3aa7a97591b5fedc3de1fa5dc982c96ff00a9de4d252425bf00fb206caa143e); fix_compile.log (patched production uark.ko, zero compiler diagnostics; the only error string is the literal -Werror flag); run.log phases B/C ELAPSED lines (2029 ms / 561 ms vs baseline 6995 ms)."
}