β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-5488

uvisor_attach/uvisor_init: up to 2Γ— synchronous 5000 ms-timeout EP0 control requests on the per-bus USB explore thread before the port exists; an evil Visor/Palm/Clie VID:PID adapter pins bus-wide device enumeration for ~5 s (10 s for SONY CLIE_35) per insertion

Summary

uvisor_init runs on the per-bus explore thread and chains sync requests at the 5000ms default (VISOR 1x, PALM4 1x, PALM35 2x) before ucom_attach: an evil adapter NAK-ing just-under-timeout pins ALL insertion/removal/enumeration events for the bus ~5-10s per insertion, repeatable by re-plug. Verbatim-production-extraction harness: VISOR 4796/4802ms, PALM4 4794/4803ms, PALM35 9591/9609ms with the exact production EP0 trace; 250ms-bound + abort fix mechanism ~250ms. Same class as DF-5407/5359.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5488 Β· 13 files
FileTypeDescriptionSize
README.md β€” 3.4 KB ↓ raw
VERDICT.md β€” 4.8 KB ↓ raw
uvisor_stall_harness.c β€” 7.2 KB view raw
Makefile β€” 127 B ↓ download
build.sh β€” 236 B view raw
run.sh β€” 136 B view raw
build.log β€” 7.1 KB view raw
run.log β€” 2.5 KB view raw
run.2.log β€” 611 B view raw
fix.diff β€” 1.8 KB view raw
fix_compile.log β€” 7.2 KB view raw
env.txt β€” 443 B view raw
verdict.json β€” 5.1 KB view raw

DF-5488 — uvisor_attach→uvisor_init: up to 2× synchronous 5000 ms-timeout

EP0 requests on the per-bus USB explore thread; an evil Visor/Palm/Clie

VID:PID adapter pins bus enumeration for ~5-10 s per insertion

The chain (production code, executed verbatim in the harness)

uvisor_attach() (uvisor.c:302-354) runs on the per-bus USB explore thread and calls uvisor_init() (uvisor.c:326 β†’ 370-517) BEFORE any ucom port exists, issuing synchronous control requests at the USB_DEFAULT_TIMEOUT of 5000 ms (usbdi.h:87):

sc_flag (VID:PID table, uvisor.c:248-278) requests uvisor.c worst
UVISOR_FLAG_VISOR (HANDSPRING VISOR 082d:0100) 1Γ— GET_CONNECTION_INFORMATION vendor IN (wLength 18) 382-389 ~5 s
UVISOR_FLAG_PALM4 (Treo, Palm m*/i705/Tungsten/Zire, Clie S360/NX60/TJ37, Garmin, Fossil, AlphaSmart, Tapwave, Samsung) 1Γ— GET_PALM_INFORMATION vendor IN (wLength 0x44) 434-442 ~5 s
UVISOR_FLAG_PALM35 (SONY CLIE_35) 2Γ— standard requests (UR_GET_CONFIG + UR_GET_INTERFACE) 476-485, 488-496 ~10 s

USB permits a device to NAK a control data stage for the entire 5 s timeout; an evil adapter (Facedancer/gadget with one of these VID:PIDs) answering each request just-under-timeout β€” or never answering β€” pins the explore thread (which serializes ALL device enumeration on that bus: exploration, insertion and removal events) for the full chain, while each request additionally holds the per-device enum lock (usb_request.c:467). On timeout the attach fails cleanly (uvisor.c:391-393 etc.), but the stall has already happened. Same class as DF-5407 (uplcom, 14Γ—5 s β‰ˆ 70 s, Medium) and DF-5359 (uipaq, ~12.8 s, Low); uvisor's worst case is 2Γ—5 s (CLIE_35), typical 5 s.

Evidence class

No Visor/Palm QEMU device model exists — verbatim-production-extraction KLD harness (the DF-5407 evidence class): build.sh sed/awk-extracts the production uvisor_init() VERBATIM from the guest's clean /usr/src/sys/bus/u4b/serial/uvisor.c (defines/structs = lines 85-182 → uv_defs.h; function = anchor-extracted → uv_init.h; extraction sha256s recorded in build.log and re-verified reproducible) and only the EP0 entry point it calls (usbd_do_request_flags, which usbd_do_request macro-expands to, usbdi.h:511-512) is re-targeted to an evil-device model (NAK ~4.9 s then answer, USB-legal). Phase B re-extracts from a scratch copy patched with fix.diff (pure uvisor_init→uvisor_init_fixed rename).

Reproduce

./build.sh   # verbatim extraction + in-tree kmod build, -Werror clean
./run.sh     # kldload; dmesg | grep HX5488

Expected (phase A, evil in-spec-slow answers): VISOR ~4.8 s, PALM4 ~4.8 s, PALM35 ~9.6 s, all err=USB_ERR_NORMAL_COMPLETION. Expected (phase B, 250 ms driver-side bound): all three ~0.25 s, err=USB_ERR_TIMEOUT (chain aborts).

Observed (two runs): VISOR 4796/4802 ms, PALM4 4794/4803 ms, PALM35 9591/9609 ms; fixed: 250/265, 259/261, 249/272 ms (run.log, run.2.log). The request trace (bmRequestType 0xc2/0x80, bRequest 0x03/0x04/0x08/0x0a, wLength 18/68/1/1) matches production exactly; the PALM4 run also shows device-supplied data flowing through the extracted code into config[].endpoint (10/5 from the 0x5A-filled answer).

Fix

fix.diff bounds the attach-path control timeout to 250 ms (UVISOR_INIT_TIMEOUT, same bound as the uplcom DF-5407 family fix) for all four call sites. The patched production uvisor.ko compiles -Werror clean (fix_compile.log).

VERDICT.md
↓ download raw

DF-5488 VERDICT β€” REPRODUCED (verbatim production code)

Finding: uvisor_attach() β†’ uvisor_init() (uvisor.c:326, 370-517) issues up to two synchronous EP0 control requests at the 5000 ms USB_DEFAULT_TIMEOUT (usbdi.h:87) on the per-bus USB explore thread, before the ucom port exists: 1Γ— vendor IN for VISOR devices (uvisor.c:382-389), 1Γ— vendor IN for PALM4 devices (uvisor.c:434-442), 2Γ— standard requests for PALM35 devices (uvisor.c:476-496). An evil adapter with one of the 28 VID:PIDs in uvisor_devs[] (uvisor.c:248-278) that NAKs the data stages pins bus-wide enumeration for the full chain (~5 s typical, ~10 s for CLIE_35), holding the per-device enum lock (usb_request.c:467) per request. Same class as DF-5407 (uplcom ~70 s, Medium) / DF-5359 (uipaq ~12.8 s, Low).

Reproduction (verbatim-production-extraction harness, DF-5407 class)

The production uvisor_init() is extracted VERBATIM from the guest's clean tree and compiled into the harness KLD; only the EP0 entry point it calls is re-targeted to an evil-device model (NAK ~4.9 s β†’ answer, USB-legal; this is the worst case because the chain then continues to the next request). Guest and audited host tree are identical (uvisor.c sha256 b742032f0dab0dc9c5cbfd63432834c00828390ddfe97f3410298f d703844438). Extraction itself is reproducible (sha256s re-recorded post-run and matching build.log: uv_defs.h 7911b455…, uv_init.h 63a03e13…).

Measured (hz=100 guest, two runs each):

variant phase A (evil) phase B (250 ms bound)
VISOR (sc_flag=0x0002) 4796 / 4802 ms 250 / 265 ms
PALM4 (sc_flag=0x0001) 4794 / 4803 ms 259 / 261 ms
PALM35 (sc_flag=0x0004) 9591 / 9609 ms 249 / 272 ms

Decisive lines (run.log / run.2.log): HX5488: PALM35 (Sony Clie 3.5): uvisor_init() DONE err=USB_ERR_NORMAL_COMPLETION ELAPSED=9591 ms HX5488: VISOR … ELAPSED=4796 ms, HX5488: PALM4 … ELAPSED=4794 ms Full EP0 trace confirms the exact production requests: bmRequestType=0xc2 bRequest=0x03 wLength=18 (GET_CONNECTION_INFORMATION), bmRequestType=0xc2 bRequest=0x04 wLength=68 (GET_PALM_INFORMATION), bmRequestType=0x80 bRequest=0x08 wLength=1 (UR_GET_CONFIG) + bRequest=0x0a (UR_GET_INTERFACE). The PALM4 run additionally shows the device-supplied endpoint bytes flowing through the verbatim code into config[].endpoint (cfg endpoints 10/5 from the 0x5A-filled response).

Why this is the production behavior

  • uvisor_attach() calls uvisor_init(sc, uaa->device, uvisor_config_copy) at uvisor.c:326 before ucom_attach; attach runs on the per-bus explore thread (established family-proven in DF-5407/DF-5359: usb_needs_explore β†’ usb_bus_explore β†’ device attach serializes the bus).
  • Each request is usbd_do_request_flags(..., USB_DEFAULT_TIMEOUT) / usbd_do_request(...) (macro β†’ same, usbdi.h:511-512), whose "WXFER" wait loop sleeps until completion or timeout (usb_request.c:642-644, 699-704). A NAK-ing device burns the full 5000 ms per request.
  • On timeout, err aborts the chain (uvisor.c:391-393, 444-446, 483-485, 494-496) and attach fails β€” but the multi-second explore-thread stall has already happened; a gadget device can also answer at 4.9 s to keep the chain going (modeled in phase A).

Severity

Low β€” bounded (~5-10 s per insertion), requires an evil/malfunctioning Visor-family adapter; family precedent (DF-5359 at ~12.8 s = Low, DF-5407 at ~70 s = Medium). Impact: bus enumeration latency / hub event thread stall, no corruption, no leak.

Fix validation

fix.diff adds UVISOR_INIT_TIMEOUT (250 ms) and uses it at all four call sites (vendor coninfo, palm info, GET_CONFIG, GET_INTERFACE). Validated two ways: 1. Harness phase B re-extracts the patched function verbatim (renamed): all three variants collapse to ~250 ms with USB_ERR_TIMEOUT and a clean chain abort. 2. The fully patched production uvisor.c compiled -Werror clean as a kmod on the guest (fix_compile.log, RC=0).

Negative results / what was checked and killed

  • PALM4 short-transfer uninit: the actlen < 12 check (uvisor.c:447-451) covers exactly the bytes later consumed (offsets 1, 8, 9 of uvisor_palm_connection_info), so no uninitialized read on that path (the unguarded sibling is the VISOR path β†’ DF-5489).
  • Device-supplied endpoint nibbles (uvisor.c:452-460) only feed usbd_transfer_setup endpoint matching (uvisor.c:333-335), which validates type/direction against the real interface descriptors.
  • Guest left clean: harness dirs removed, production uvisor.c untouched.

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

fixed
baseline reproduced→ patch + rebuild →patched reproduced

fix.diff applied to a scratch copy of the guest's /usr/src uvisor.c: the patched function re-extracted verbatim (renamed) runs at 250/265, 259/261, 249/272 ms with USB_ERR_TIMEOUT aborts (vs 4.8-9.6 s baseline), and the fully patched production uvisor.ko compiled -Werror-clean RC=0 (fix_compile.log). The multi-second explore-thread occupancy is GONE under the fix.

fix.diff (git apply --check clean on host; patch -p5 clean on guest); run.log/run.2.log phase-B lines; fix_compile.log (RC=0); VERDICT.md 'Fix validation'.
↓ fix.diffper-fix-DF-5488

Confirmed kernel references

Detail

Exploit chain

Evil USB adapter (Facedacer/gadget) presenting a VID:PID from uvisor_devs[] (uvisor.c:248-278: HANDSPRING VISOR/Treo, PALM m*/Tungsten/Zire, SONY CLIE_35, etc.) -> uvisor_attach() runs uvisor_init() on the per-bus USB explore thread -> the adapter NAKs each control data stage for just-under the 5 s timeout (or never answers) -> bus-wide device enumeration (explore/insert/remove events) is pinned ~5 s per request, up to ~10 s for a CLIE_35-VID:PID device, per insertion; repeatable by re-plugging.

Evidence (decisive lines)

run.log/run.2.log: 'HX5488: PALM35 (Sony Clie 3.5): uvisor_init() DONE err=USB_ERR_NORMAL_COMPLETION ELAPSED=9591/9609 ms', 'VISOR ... 4796/4802 ms', 'PALM4 ... 4794/4803 ms'; full per-request EP0 trace with bmRequestType/bRequest/wLength matching uvisor.c production call sites; phase-B fixed lines '~250 ms err=USB_ERR_TIMEOUT'; build.log: verbatim-extraction sha256s + -Werror kmod build; fix_compile.log: patched production uvisor.ko RC=0.

PoC changes

No seed PoC existed; authored from scratch on the DF-5407 verbatim-extraction pattern. Bring-up fixes: (1) USB_ERR_OK -> USB_ERR_NORMAL_COMPLETION (DF u4b enum name); (2) missing uv_init_fixed.h include; (3) usb_process.h/condvar.h/stdint.h include order; (4) a '/' sequence inside my own header comment (the text 'Treo/Palm m/Clie') terminated the C comment and broke the parse -- reworded; all documented in build.log history.

Verified recommended fix

Bound the attach-path control-request timeout to 250 ms (UVISOR_INIT_TIMEOUT) at all four uvisor_init() call sites (full diff in fix.diff).

Verdict

REPRODUCED with the production code VERBATIM (DF-5407 evidence class): uvisor_init() is sed/awk-extracted unchanged from the guest's clean tree (extraction sha256s recorded and re-verified; guest tree == audited tree, uvisor.c sha256 b742032f...) and only its EP0 entry point (usbd_do_request_flags, to which usbd_do_request macro-expands, usbdi.h:511-512) is re-targeted to an evil-device model that NAKs ~4.9 s then answers (USB-legal worst case: the chain continues). Phase A per-insertion explore-thread occupancy: VISOR 4796/4802 ms (1 x GET_CONNECTION_INFORMATION, uvisor.c:382-389), PALM4 4794/4803 ms (1 x GET_PALM_INFORMATION, uvisor.c:434-442), PALM35 9591/9609 ms (UR_GET_CONFIG + UR_GET_INTERFACE, uvisor.c:476-496) -- all at the 5000 ms USB_DEFAULT_TIMEOUT (usbdi.h:87) with each request serializing behind the per-device enum lock (usb_request.c:467). The EP0 trace (0xc2/0x03/len18, 0xc2/0x04/len68, 0x80/0x08, 0x80/0x0a) matches production exactly, and the PALM4 run shows device-supplied endpoint bytes flowing through the verbatim code into config[].endpoint. Fix mechanism (UVISOR_INIT_TIMEOUT 250 ms bound at all four call sites): all variants collapse to ~250 ms with USB_ERR_TIMEOUT and a clean chain abort; fully patched production uvisor.ko compiled -Werror-clean RC=0.