ucycom_attach pins the per-bus USB explore thread ~5 s per insertion via the HID report-descriptor fetch: an evil Earthmate NAKing (or slowly answering) GET_DESCRIPTOR(REPORT) stalls bus-wide device enumeration for the full USB_DEFAULT_TIMEOUT (family minimum: exactly one request)
Summary
The attach's first bus transaction is the synchronous descriptor fetch at the 5000ms default on the per-bus explore thread: an evil adapter answering at 4.9s makes the request SUCCEED while still pinning all bus enumeration (harness: 4798/4801ms succeeding, 4903/4910ms silent). Same mechanism as DF-5359/5488/5407; ucycom is the smallest member. Core-level 1000ms bound in usbd_req_get_report_descriptor benefits ums/ukbd/wsp/uhid/uaudio identically.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5505 Β· 14 files| File | Type | Description | Size | |
|---|---|---|---|---|
| ucycom_attachstall_harness.c | β | 9.7 KB | view raw | |
| Makefile | β | 114 B | β download | |
| extract.sh | β | 917 B | view raw | |
| build.sh | β | 205 B | view raw | |
| run.sh | β | 186 B | view raw | |
| build.log | β | 7.1 KB | view raw | |
| extract.log | β | 206 B | view raw | |
| run.log | β | 1.2 KB | view raw | |
| run.2.log | β | 840 B | view raw | |
| run.patchedkernel.log | β | 893 B | view raw | |
| fix_kernel.log | β | 349.3 KB | β download | |
| fix.diff | β | 905 B | view raw | |
| env.txt | β | 360 B | view raw | |
| VERDICT.md | β | 4.8 KB | β raw |
DF-5505 VERDICT β ucycom_attach explore-thread stall (reproduced, mechanism-level)
Finding
ucycom_attach() (ucycom.c:202-285) runs in newbus attach context on the
per-bus USB explore (hub event) thread when an Earthmate β the ONLY VID:PID in
ucycom_devs[] (ucycom.c:177-179), VID 0x1163 DeLorme β is inserted. Its very
first bus transaction is the synchronous HID report-descriptor fetch
error = usbd_req_get_hid_desc(uaa->device, NULL, &urd_ptr, &urd_len,
M_USBDEV, UCYCOM_IFACE_INDEX); /* ucycom.c:231-233 */
usbd_req_get_hid_desc (usb_hid.c:801-843) allocates a device-declared-length
buffer and calls usbd_req_get_report_descriptor (usb_hid.c:834-835), whose
terminal call is usbd_do_request(udev, lock, &req, d)
(usb_request.c:1892) β the macro form of
usbd_do_request_flags(β¦, USB_DEFAULT_TIMEOUT) with
USB_DEFAULT_TIMEOUT = 5000 ms (usbdi.h:87, 511-512). An evil Earthmate that
NAKs the control data stage pins the explore thread β and therefore bus-wide
device enumeration for every device on that bus β for the FULL 5 s timeout per
insertion; a device answering at just-under-timeout makes the request SUCCEED
and pins the thread anyway. This is the DF-5359 (uipaq, 12.8 s)/DF-5407
(uplcom, 70 s)/DF-5488 (uvisor, 5β10 s) mechanism; ucycom is the family
minimum: exactly ONE request, so the per-insertion cost is ~5 s.
The remainder of the attach path (three hid_report_size() calls + validation,
ucycom.c:241-254) is CPU-only and measured negligible (the harness runs it
verbatim against the real kernel hid_report_size, usb_hid.c:558).
Reproduction (KLD-harness-with-production-code, DF-5488 class)
The harness runs VERBATIM, sed-extracted at build time from the guest's
md5-gated /usr/src (ucycom.c c7b432ceβ¦, usb_hid.c 3329879bβ¦):
usbd_req_get_hid_descβ usb_hid.c:801-843 (the core function ucycom calls);- the ucycom.c:229-254 attach EP0 block (fetch + error path + report-size
validation) with locals named exactly as ucycom_attach declares them,
running against the REAL kernel
hid_report_size()with a synthetic 21-byte HID report descriptor (ilen=27, olen=27, flen=5 β passes the verbatim validation).
Interposed models only at the hardware boundary:
usbd_req_get_report_descriptor (the EP0 sink whose real timeout is the finding),
usbd_get_iface / usbd_get_config_descriptor / hid_get_descriptor_from_usb
(fake 21-byte HID interface), device_printf (kprintf stand-in). The model
sleeps min(evil_delay, core_cap) in the "WXFER" wait and answers or times out
exactly as the real usb_request.c timeout loop would.
Measured (run.log, run.2.log β fresh guest)
- Phase A (evil NAKs 4.9 s then answers β request SUCCEEDS, no error visible): explore-thread occupancy 4798 / 4801 ms, attach CONTINUES with validated report sizes i=27 o=27 f=5.
- Phase B (evil silent): 4903 / 4910 ms (the 5000 ms
USB_DEFAULT_TIMEOUTat hz=100 tick granularity), attach ABORTs through the verbatim error path (ucycom.c:235-240 β 279-284). - Phase C (fix modeled: core cap 1000 ms): 990β1000 ms, fail-fast abort.
Fix validation
- fix.diff bounds the report-descriptor fetch at usb_request.c:1892
(
usbd_do_request_flags(β¦, 1000)instead of the 5 s default);git apply --checkclean on the audited repo. - Compile-level: patched usb_request.c (md5 4755628aβ¦) spliced into the guest
tree,
make nativekernel KERNCONF=X86_64_GENERIC NO_CLEAN=1RC=0 in 146 s (fix_kernel.log),make installkernel+ reboot into the patched kernel + both harnesses re-run unchanged (run.patchedkernel.log) β no regression. - Behavioral proof at mechanism level: phase C 4.9β5.0 s β ~1.0 s.
- The fix benefits the other
usbd_req_get_hid_descconsumers equally (ums.c:444/616, ukbd.c:1033/1260, wsp.c:728, uhid.c:768, uaudio.c:6101) and bounds only an enumeration-time descriptor read, which any functional device serves in milliseconds. - Scope caveat: compile + mechanism level; no physical crafted Earthmate can be attached to this guest.
Bring-up note
The first DF-5505 harness revision panicked the guest
(panic: lockmgr: LK_RELEASE: no lock held): the evil-device model called
lksleep() on a lock it did not hold. The DF-5504 model never hit this because
the taskqueue thread legitimately holds sc->sc_lock across callbacks. The
model was fixed to acquire/release around the sleep exactly as the production
requester does; the guest was reset (vm.sh reset with-src) and all reported
numbers are from the fresh guest.
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 the audited repo (git apply --check passes; the guest's own patch(1) balked at the well-formed hunk so the host-built patched file -- md5 4755628a... -- was spliced byte-identically). Compile-level: the patched usb_request.c built into a full X86_64_GENERIC kernel on the guest (make nativekernel NO_CLEAN=1, RC=0, 146 s; fix_kernel.log), was installed with make installkernel, booted, and both harnesses re-ran unchanged on the patched kernel (run.patchedkernel.log) -- no regression. Behavioral proof at mechanism level: harness phase C (core cap 1000 ms) measures 990-1000 ms fail-fast vs 4.8-5.0 s baseline. Scope caveat: mechanism + compile level per the family evidence class; a physical crafted Earthmate cannot be attached to this guest.
fix.diff (sha256 c662e587eb8e1e579c6d36cd6090896c04da94c5f5b2b01fb3816fa3c4cf5197); fix_kernel.log (nativekernel RC=0, 145.79 real); run.patchedkernel.log (patched-kernel re-run); run.log/run.2.log 'UA5505[C]: attach ABORT ... 994/990/1000 ms' vs 'UA5505[A]: ... 4798/4801 ms' and 'UA5505[B]: ... 4903/4910 ms'; VERDICT.md 'Fix validation' section
Confirmed kernel references
- sys/bus/u4b/serial/ucycom.c:202-285
- sys/bus/u4b/serial/ucycom.c:229-254
- sys/bus/u4b/serial/ucycom.c:231-233
- sys/bus/u4b/usb_hid.c:801-843
- sys/bus/u4b/usb_hid.c:834-835
- sys/bus/u4b/usb_request.c:1876-1893
- sys/bus/u4b/usb_request.c:1892
- sys/bus/u4b/usbdi.h:87
- sys/bus/u4b/usbdi.h:511-512
- sys/bus/u4b/usb_hid.c:558-605
Detail
Exploit chain
attacker physically inserts an evil USB adapter presenting the DeLorme Earthmate VID:PID; uhub hands it to ucycom_attach on the per-bus USB explore thread; the device NAKs (or slowly answers) the GET_DESCRIPTOR(REPORT) control data stage; the synchronous 5 s-timeout request pins the explore thread ~4.8-5.0 s per insertion, stalling enumeration of every other device on that bus; repeatable at will by re-insertion
Evidence (decisive lines)
run.log/run.2.log lines 'UA5505[A]: attach CONTINUES ... after 4798 ms of explore-thread occupancy' (request SUCCEEDED), 'UA5505[B]: attach ABORT ... 4903 ms' (silent: full USB_DEFAULT_TIMEOUT), 'UA5505[C]: ... 994/990/1000 ms' (fix modeled); extract.log shows the verbatim sed-extraction of usb_hid.c:801-843 and ucycom.c:229-254 from the md5-gated guest tree; fix_kernel.log shows the patched usb_request.c building into a full kernel RC=0; VERDICT.md narrates the call chain with line cites and the bring-up panic note
PoC changes
No seed PoC existed; authored from scratch on the DF-5488 verbatim-extraction pattern (usb_hid.c:801-843 and ucycom.c:229-254 sed-extracted md5-gated from the guest's own /usr/src; usbd_req_get_report_descriptor/usbd_get_iface/usbd_get_config_descriptor/hid_get_descriptor_from_usb/device_printf interposed as models). Build fixups: add sys/malloc.h (nested-extern on kmalloc/kfree), usb_serial.h + usb_process.h + sys/condvar.h for the embedded struct types. First revision panicked the guest (model lksleeped on a lock it did not hold -> 'panic: lockmgr: LK_RELEASE: no lock held'); fixed to acquire/release around the WXFER sleep as the production requester does, guest reset with-src, and all reported numbers are from the fresh guest.
Verified recommended fix
Bound the enumeration-time report-descriptor fetch: usbd_do_request_flags(..., 1000) at usb_request.c:1892 instead of the 5 s USB_DEFAULT_TIMEOUT; full diff in fix.diff
Verdict
Mechanism-level reproduction with production code verbatim (DF-5488 evidence class): ucycom_attach() (ucycom.c:202-285) runs on the per-bus USB explore (hub event) thread at insertion of an evil DeLorme Earthmate (the only VID:PID in ucycom_devs[], ucycom.c:177-179) and its first bus transaction is the synchronous HID report-descriptor fetch at ucycom.c:231-233 -> usbd_req_get_hid_desc (usb_hid.c:801-843, run VERBATIM by the harness) -> usbd_req_get_report_descriptor, whose terminal usbd_do_request (usb_request.c:1892) carries the default USB_DEFAULT_TIMEOUT = 5000 ms (usbdi.h:87,511-512). The harness ran the VERBATIM core function plus the VERBATIM ucycom.c:229-254 attach block (against the real kernel hid_report_size, usb_hid.c:558) with only the EP0 sink modeled: an evil device that NAKs 4.9 s and then answers -- so the request SUCCEEDS and no error is visible anywhere -- pinned the simulated explore thread 4798/4801 ms per insertion with attach CONTINUING into the verbatim report-size validation (i=27 o=27 f=5); a fully silent evil device pinned it 4903/4910 ms (the full 5 s core timeout) before the verbatim error path aborts. This pins bus-wide device enumeration for every device on that bus (DF-5359/DF-5407/DF-5488 mechanism); ucycom is the family minimum with exactly one request (~5 s per insertion, vs uipaq 12.8 s Low and uplcom 70 s Medium). fix.diff bounds the fetch at usb_request.c:1892 (usbd_do_request_flags(...,1000)): git-apply-clean on the audited repo, compiled into a full kernel on the guest (make nativekernel NO_CLEAN=1, RC=0, 146 s, fix_kernel.log), installed and rebooted into with both harnesses re-run unchanged (run.patchedkernel.log), and modeled in harness phase C at 990-1000 ms fail-fast. The fix equally bounds the same fetch for ums/ukbd/wsp/uhid/uaudio, and only affects an enumeration-time descriptor read any functional device serves in milliseconds. No Cypress device model exists on the guest, hence the harness evidence class; availability-only impact, Low per family calibration.
No comments yet.