# DF-5487 VERDICT — REPRODUCED (mechanism-level, production machinery)

**Finding:** `uvisor_cfg_close()` issues one synchronous EP0 vendor
control-IN (`CLOSE_NOTIFICATION`) at a 1000 ms timeout with the error
discarded (uvisor.c:529-545), and `ucom_close()` waits for it
uninterruptibly (`usb_proc_mwait`, usb_serial.c:671-672 + 955-957).
An evil Handspring/Palm-VID:PID device (or a wedged real one) that NAKs
the vendor data stage stretches every close(2) to ~1 s SIGKILL-immune and
stalls the unplug (hub event thread) drain identically.

## Reproduction (KLD-harness-with-production-code, family evidence class)

No QEMU Visor/Palm device model exists (`-device usb-serial` is a
Netchip 0525:7701 that binds uftdi), so the harness runs the **real
in-kernel usb_process machinery** — `usb_proc_create` / `usb_proc_msignal`
/ `usb_proc_mwait` / `usb_proc_drain` (usb_process.c) — that ucom's
synchronous close and detach drain go through, with uvisor's exact
production chain shape (0 requests + core hz/10 pause per open chain;
0-request HUPCL line task + 1×1000 ms request per close chain). One
timed-out EP0 request is modeled by its exact production cost
(`lksleep("WXFER", N ms)`, usb_request.c:642-644).

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

| phase | closer unblocked | expected |
|-------|------------------|----------|
| A baseline (production chain) | **1080 / 1093 ms** | ~1100 ms |
| B fix mechanism (250 ms bound) | **346 / 363 ms** | ~350 ms |

Decisive lines (run.log / run.2.log):
`HX5487[A]: closer: unblocked  ELAPSED=1080 ms` … `=1093 ms`
`HX5487[B]: closer: unblocked  ELAPSED=346 ms` … `=363 ms`

Task-level trace shows the exact production ordering: cfg-open task
(EMPTY + hz/10 pause) → line tasks (0 reqs) → cfg-close task (1×1000 ms,
"error DISCARDED") with the closer blocked in `usb_proc_mwait` throughout.

## Why this is the production behavior

- `uvisor_cfg_close()` request: uvisor.c:539-540 (`ucom_cfg_do_request(...,
  buffer, 0, 1000)`), error path is DPRINTFN-only (uvisor.c:541-544).
- `ucom_cfg_do_request` = `usbd_do_request_proc(udev, &sc_super->sc_tq,
  req, ptr, flags, NULL, timo)` (usb_serial.h:220-221) — synchronous
  control transfer on the ucom taskqueue.
- A NAK-ing device makes the request eat the full timeout:
  usb_request.c:642-644 (`while (usbd_transfer_pending(xfer))
  lksleep(xfer, ..., "WXFER", hz)`) + 699-704.
- close(2) synchronicity: usb_serial.c:671-672 (`if (fn == ucom_cfg_close)
  usb_proc_mwait(...)`), reached from ucom_close() at usb_serial.c:955-957;
  mwait is cv_wait — uninterruptible, SIGKILL-immune (family-proven in
  DF-5421/5424/5425/5449/5456/5476).
- Unplug: uvisor_detach (uvisor.c:356-368) → ucom_detach →
  `usb_proc_drain(&ssc->sc_tq)` (usb_serial.c:394) waits out the same
  request on the hub event thread; every request also serializes behind
  the per-device enum lock (usb_request.c:467).

## Severity

Low — consistent with the family (weakest member: ~1 s per close cycle,
bounded; requires an evil/malfunctioning Handspring/Palm-VID:PID device
physically attached or passed through). No memory corruption, no info
leak; impact is bounded uninterruptible taskqueue/thread occupancy
(DoS), repeatable per open/close cycle and on unplug.

## Fix validation

`fix.diff` adds `UVISOR_CFG_TIMEOUT` (250 ms) and uses it for the close
notification. Validated two ways:
1. Harness phase B models the fixed chain: closer ELAPSED drops
   1080→346 / 1093→363 ms. The multi-second→bounded occupancy change is
   the fix mechanism working.
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

- No DF-5384-class "detach on never-attached" bug: `ucom_detach()` early-
  returns when `!(ssc->sc_flag & UCOM_FLAG_ATTACHED)` (usb_serial.c:376-377),
  so uvisor_attach's error-path `uvisor_detach()` (uvisor.c:351-353) is safe
  even when `ucom_attach()` never ran or failed internally (it calls
  `ucom_detach()` itself at usb_serial.c:346).
- No cfg_param/status/line chains exist to stall (callback table
  uvisor.c:224-231) — tcsetattr is free for uvisor.
- Guest left clean: harness dirs removed, production uvisor.c untouched
  (md5 28d0d390d06dd00f9dbfc03106b453a7 before and after).

## 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.
