umcs7840_cfg_open/cfg_close chain of 28/4 sequential synchronous 500ms EP0 requests: evil device stretches every port open to ~14 s and blocks close(2) and unplug (hub event thread) uninterruptibly for ~16 s per cycle
Summary
cfg_open = 28 sequential sync EP0 requests (cfg_close adds 4), each 500ms timeout: a NAK-forever evil device (or a dialer-group user looping open/close on cuaU*) stretches every open to ~14s with close(2) and unplug waiting UNINTERRUPTIBLY against the same queue - the DF-5367 mechanism, umcs instance. Harness-proven: closer blocked 15702/15707/15700ms; 100ms-timeout fix caps the chain at ~3.2s. Bound-the-timeout fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5383 Β· 13 files| File | Type | Description | Size | |
|---|---|---|---|---|
| umcs_cfgstall_harness.c | β | 6.8 KB | view raw | |
| Makefile | β | 104 B | β download | |
| build.sh | β | 184 B | view raw | |
| run.sh | β | 149 B | view raw | |
| build.log | β | 7.1 KB | view raw | |
| fix_compile.log | β | 911 B | view raw | |
| run.log | β | 2.1 KB | view raw | |
| run.2.log | β | 1.2 KB | view raw | |
| env.txt | β | 188 B | view raw | |
| fix.diff | β | 694 B | view raw | |
| README.md | β | 3.2 KB | β raw | |
| VERDICT.md | β | 4.7 KB | β raw | |
| verdict.json | β | 4.2 KB | view raw |
DF-5383 β umcs7840 cfg_open/cfg_close chain of up to 28/4 synchronous 500 ms EP0 requests stalls close(2) and unplug (hub event thread) for ~16 s
What (finding in one line)
umcs7840_cfg_open() (sys/bus/u4b/serial/umcs.c:410-515) performs 28
synchronous EP0 control requests on first open (2 global-config at
umcs.c:423,426 + 14 port init at umcs.c:431-483 + 6 baud-rate writes at
umcs.c:1022-1046 via umcs7840_set_baudrate(9600) at umcs.c:487 + 6 tail
requests at umcs.c:496-512), each via usbd_do_request_proc()
(umcs.c:927/948/972/997) with UMCS7840_CTRL_TIMEOUT = 500 ms (umcs.h:33).
umcs7840_cfg_close() adds 4 more (umcs.c:527-534). A USB device may NAK
the data stage of a control transfer arbitrarily long, so an evil MCS7840
can make every request succeed in just-under-500 ms without tripping
the error-abort paths (cfg_open/cfg_close only abort on hard errors).
The whole chain runs on sc_super_ucom.sc_tq (usb_serial.c:322), and tty
close is synchronous against that queue: ucom_close() β
ucom_queue_command(ucom_cfg_close) β usb_proc_mwait()
(usb_serial.c:671-672) cv_wait()s uninterruptibly (usb_process.c:385-393)
behind the still-running cfg_open. Unplug racing a cfg task stalls the
same way through ucom_detach() β usb_proc_drain() (usb_serial.c:394)
on the USB hub event thread β the DF-5367 (ufoma) mechanism, umcs
instance. Worst case ~16 s uninterruptible stall per open/close cycle
(28 + 4 requests Γ 500 ms, + hz/10 pause at usb_serial.c:777).
Evidence class
No MCS7840 device model on the QEMU guest (as uipaq/ufoma) β KLD harness
built from production code: the real usb_proc_create/msignal/mwait
machinery of ucom's taskqueue, with the per-request cost mirroring
usbd_do_request_flags's WXFER sleep for a request the device stretches
to just under the 500 ms timeout (usb_request.c:567-708).
Build
mkdir -p /usr/src/sys/bus/u4b/serial/hx5383 cp umcs_cfgstall_harness.c Makefile /usr/src/sys/bus/u4b/serial/hx5383/ sh build.sh # in-tree kmod build, -Werror clean
Run
sh run.sh # kldload ./umcscfgstall.ko; kldunload; dmesg
Expected output (decisive lines)
HX5383[A]: closer: queue cfg_close + usb_proc_mwait() (uninterruptible, usb_serial.c:671-672) HX5383[A]: closer: unblocked ELAPSED=15702 ms HX5383[B]: closer: unblocked ELAPSED=3228 ms
Phase A (500 ms/request, production constant) MUST show the closer blocked ~15.6-16 s. Phase B (fix mechanism from fix.diff: timeout bounded to 100 ms) MUST show ~3.2 s. run.log holds three loads: 15702/15707/15700 ms vs 3228/3232/3213 ms.
Fix validation
fix.diff(againstumcs.h) βgit apply --checkpasses; patched driver compiles-Werror-clean as a kmod on the guest (fix_compile.log, RC=0).- Harness Phase B shows the bounded-timeout mechanism: 15.7 s β 3.2 s.
Artifacts
umcs_cfgstall_harness.cβ the KLD harness (annotated with the umcs.c / usb_serial.c / usb_request.c lines it mirrors)Makefile,build.sh,run.shbuild.log,fix_compile.log,run.log(decisive run, 3 loads),run.2.log,env.txtfix.diffβ bound UMCS7840_CTRL_TIMEOUT to 100 ms with rationaleVERDICT.md,manifest.json,verdict.json
DF-5383 VERDICT β umcs cfg_open/cfg_close sync-request chain stalls close(2)/detach ~16 s
Status: reproduced (mechanism level, production code) β impact: dos (evil USB device), no memory corruption.
The bug, link by link
-
The chain.
umcs7840_cfg_open()issues these synchronous control requests in order (each = one EP0 round trip throughumcs7840_get/set[_UART]_reg_sync()βusbd_do_request_proc()): umcs.c:423 (get CONTROL1, first open only), :426 (set CONTROL1, first open only), :431, :434, :437 (SP reset toggle), :441 (SCRATCH RS232), :445, :448 (CONTROLx RX-disable), :452 (IER=0), :456, :458 (FCR), :465 (LCR), :473 (MCR), :477, :480, :483 (FIFO reset), thenumcs7840_set_baudrate(9600)at :487 β umcs.c:1022, 1027, 1033, 1037, 1040, 1046 (clock select + DLL/DLM), then :496 (get LCR), :498 (set IER), :503, :506 (CONTROLx RX-enable), :510 (LSR), :512 (MSR) β 28 requests on first open, 26 later.umcs7840_cfg_close()adds 4 (umcs.c:527, 528, 531, 534). -
The timeout. Every request carries
UMCS7840_CTRL_TIMEOUT= 500 ms (umcs.h:33). Inusbd_do_request_flags()the requester sleeps in the "WXFER" loop (usb_request.c:642-644) until the transfer completes or the timeout fires (usb_request.c:700-704). USB permits a device to NAK the data stage indefinitely, so an evil device can make each request succeed just under the timeout β cfg_open'sif (err) return;guards never fire, and the full chain costs ~28 Γ 500 ms β 14 s (+hz/10trailing pause in ucom_cfg_open, usb_serial.c:777). -
Who blocks. All cfg tasks run on the per-adapter ucom taskqueue
sc_super_ucom.sc_tq(created at usb_serial.c:322; tasks execute serialized, message staying head-of-queue during execution, usb_process.c:146-158). * close(2) of the tty is synchronous:ucom_close()βucom_queue_command(ucom_cfg_close)βusb_proc_mwait()(usb_serial.c:671-672)cv_wait()s β uninterruptibly (usb_process.c:385-393) β until the cfg_close message drains, i.e. behind the running cfg_open (14 s) plus cfg_close itself (4 Γ 500 ms β 2 s) β 16 s. * unplug racing an in-flight cfg task:umcs7840_detach()βucom_detach()βusb_proc_drain()(usb_serial.c:394) β the USB hub event thread waits for the task to finish: same ~14-16 s per cycle (identical mechanism to DF-5367/ufoma, different driver and cause). * Reachability: open/close of/dev/ttyU*requires root/wheel (0600);/dev/cuaU*is 0660 uucp:dialer β a dialer-group user can drive the cycle; the pure evil-device variant needs no privileges at all, just insertion.
Why the harness is faithful
Production usb_proc_create/usb_proc_msignal/usb_proc_mwait are used
verbatim (the same functions ucom's taskqueue and synchronous close use).
Per-request cost mirrors a production "device stretches the data phase to
just under the timeout": one lksleep(..., "WXFER", USB_MS_TO_TICKS(500)).
Request counts (28 open + 4 close) are counted from the cited umcs.c
lines. The closer thread reproduces usb_serial.c:671-672's mwait call
pattern exactly.
Measured results (run.log, 3 module loads)
| phase | closer blocked |
|---|---|
| A baseline (500 ms/request = production) | 15702 / 15707 / 15700 ms |
| B fix mechanism (fix.diff: 100 ms/request) | 3228 / 3232 / 3213 ms |
Expected β 27Γ500 + 100 + 4Γ500 = 15600 ms (A) and β 27Γ100 + 100 + 4Γ100 = 3200 ms (B). Match (the closer queues 100 ms into cfg_open).
Fix validation
fix.diffboundsUMCS7840_CTRL_TIMEOUTto 100 ms (one-line change in umcs.h with rationale): worst-case open+close chain drops from ~16 s to ~3.2 s while staying β« a healthy device's control latency (Β΅s-ms). Full remedy would additionally bound the whole cfg sequence; kept minimal here.git apply --checkpasses; patched driver (umcs.c + patched umcs.h) compiles-Werror-clean as a kmod on the guest (fix_compile.log).- Harness Phase B: 15.7 s β 3.2 s.
- Scope caveat: mechanism + compile level (no MCS7840 device model on the guest), same sanctioned class as DF-5367.
Impact ceiling
Repeated ~16 s uninterruptible stalls of user threads closing the device and of the USB hub event thread during unplug, driven by an evil device (or a dialer-group user looping open/close); no memory corruption, no leak. Bounded per cycle, hence Low.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
fixedfix.diff is git-apply-able against sys/ (git apply --check passes); the patched driver (pristine umcs.c + patched umcs.h) compiles -Werror-clean as a kmod on the guest (fix_compile.log RC=0); harness Phase B with the bounded timeout measures closer block 15702-class ms -> 3228-class ms (5x reduction of the stall ceiling). Scope caveat: mechanism+compile level; end-to-end with a physical crafted device is not possible on this guest.
fix.diff (sha256 ab01b449...); fix_compile.log; run.log 'HX5383[B] ELAPSED=3228/3232/3213 ms'; VERDICT.md 'Fix validation' section
Confirmed kernel references
Detail
Exploit chain
evil USB device (MCS7840 VID/PID) enumerates normally, then answers every vendor control request with the data stage NAK'd for ~499 ms -> open(/dev/ttyUx.0) queues cfg_open costing ~14 s on the ucom tq; close(2) (root/wheel on ttyU, dialer group on cuaU) blocks uninterruptibly ~16 s behind it via usb_proc_mwait; unplug mid-cycle wedges the USB hub event thread identically via usb_proc_drain. Repeatable per open/close cycle. No memory corruption.
Evidence (decisive lines)
run.log lines 'HX5383[A]: closer: unblocked ELAPSED=15702/15707/15700 ms' vs 'HX5383[B]: ... 3228/3232/3213 ms'; VERDICT.md counts all 28+4 requests with umcs.c line cites and traces the close/unplug blocking path; build.log/fix_compile.log show -Werror-clean builds of harness and patched driver
PoC changes
No seed PoC existed; authored from scratch. Built clean on the first compile after applying the DF-5367 harness lessons (bsd.kmod.mk, opt_usb.h/device_if.h/bus_if.h SRCS).
Verified recommended fix
Bound UMCS7840_CTRL_TIMEOUT to 100 ms (one-line umcs.h change, full diff in fix.diff) so the 28+4-request open/close chain cannot exceed ~3.2 s; optionally also cap the whole cfg_open sequence
Verdict
Mechanism-level reproduction with production code: umcs7840_cfg_open() chains 28 synchronous EP0 control requests on first open (umcs.c:423-512 incl. 6 from umcs7840_set_baudrate at umcs.c:1022-1046) and umcs7840_cfg_close() 4 more (umcs.c:527-534), each with UMCS7840_CTRL_TIMEOUT=500ms (umcs.h:33). An evil device can NAK each control data stage to just under the timeout so every request succeeds slowly and no error-abort fires, making one open+close cost ~16s of ucom-taskqueue occupancy. A harness using the production usb_proc_create/msignal/mwait machinery with the production per-request cost shows the closer thread (ucom_close's synchronous usb_proc_mwait at usb_serial.c:671-672, uninterruptible cv_wait at usb_process.c:385-393) blocked 15702/15707/15700 ms across three runs; unplug racing a cfg task stalls the hub event thread the same way via ucom_detach's usb_proc_drain (usb_serial.c:394) -- the DF-5367 mechanism, umcs instance. Fix mechanism (timeout bounded to 100 ms, fix.diff) measures 3228/3232/3213 ms. End-to-end with a physical crafted MCS7840 is not possible on this guest (no device model), hence the KLD-harness evidence class.
No comments yet.