# 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

```c
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_TIMEOUT`
  at 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 --check` clean 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=1` RC=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_desc` consumers 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.
