# VERDICT — DF-5367 (ufoma detach-stall)

**status: reproduced (mechanism-level, production `usb_process` code) — impact: dos**

## Reproduced? Yes — how and why

The full end-to-end chain needs a crafted MCPC USB device (interface class
0x02 / subclass 0x88). This guest cannot attach one (usb-serial binds
`uftdi`, established in DF-5359), so per the sanctioned evidence classes the
PoC is a **KLD harness built from production code** that instantiates the
exact kernel machinery the finding is about:

* `usb_proc_create()` + `usb_proc_msignal()` — the same ucom cfg-task
  process (`sc_tq`) that `ucom_attach()` creates (usb_serial.c:322) and
  `ucom_queue_command()` feeds (usb_serial.c:635-659);
* a cfg task whose body is **ufoma.c:549-550 verbatim**:
  `cv_timedwait(&sc->sc_cv, &sc->sc_lock, UFOMA_MAX_TIMEOUT * hz)` with no
  `usb_proc_is_gone()` / device-removal abort — exactly what
  `ufoma_cfg_open()` → `ufoma_cfg_link_state()`/`ufoma_cfg_activate_state()`
  (ufoma.c:837-842) and `ufoma_cfg_close()` → `ufoma_cfg_activate_state()`
  (ufoma.c:846-851) execute on the ucom cfg thread;
* `usb_proc_drain()` — the exact drain `ucom_detach()` calls at
  usb_serial.c:394 on the `ufoma_detach()` path (ufoma.c:480).

Measured (run.log, run.2.log):

| phase | expectation | run 1 | run 2 |
|---|---|---|---|
| A baseline: drain after "unplug" 2 s into the 15 s wait | ≈ 13 s | **13000 ms** | **13005 ms** |
| B fix mechanism (dying flag + `cv_broadcast` before drain) | ≈ 0 s | **0 ms** | **0 ms** |

Why the mechanism is exact (kernel refs):

* `usb_proc_drain()` sets `up_gone` and sleeps on `up_cv` until the process
  **thread exits** (usb_process.c:424-442);
* the thread (usb_process.c:146-170) executes the callback to completion
  before it can ever re-check `up_gone` — a callback blocked in a 15 s
  `cv_timedwait` delays the drain by the full remainder;
* the wakeup never comes from the driver side: the only `cv_signal(&sc_cv)`
  is in `ufoma_intr_callback()` (ufoma.c:696) for the vendor ACK
  notification; on unplug the intr xfer completes `USB_ERR_CANCELLED` and
  the callback simply returns (ufoma.c:601-602, 758-764).

So: unplug (or `kldunload`, or shutdown) racing an open/close within the
~2-18 s cfg-task window stalls `ufoma_detach()` — which runs on the USB hub
event thread — for the remainder of the waits (1 s link + 15 s activate +
0.1 s pause, plus up to 2×1 s control-request timeouts on a NAKing device).
An evil device that withholds the ACK and force-disconnects on its own
timing (a device can drop D+/D− at will) makes this deterministic per
insertion cycle; repeatable cycles yield sustained USB event-thread
starvation. Worst-case single-event block measured/derived: **~16-18 s**.

## Exploit chain

Not applicable (no memory corruption). Escalation ceiling: sustained
system-wide USB hotplug/event denial plus a 16-18 s wedge of the hub event
thread per cycle, triggered by an evil (or merely flaky) MCPC device plus
any local open()/close() of the `cuaU*` node (any user; the callout nodes
are world-accessible by default) — or simply an unplug racing a user's
open()/close().

## Fix validation

* `fix.diff` (authored after verification): slices the ACK waits into 1 s
  pieces with a `sc_dying` abort, and `ufoma_detach()` sets `sc_dying` and
  `cv_broadcast(&sc_cv)` **before** `ucom_detach()`, so drain completes
  immediately.
* `git apply --check` passes against the read-only `sys/` tree.
* The patched driver compiles **-Werror-clean** as a kmod on the guest
  (build output in the run transcript).
* Harness Phase B runs the patched pattern against the same production
  drain machinery: **13000 ms → 0 ms**.
* Full end-to-end patched-kernel validation is not possible on this guest
  (no MCPC device can attach; a rebuilt kernel would not change the harness,
  which exercises usb_process, not ufoma). Hence fix confidence is
  mechanism+compile level, not device level.

## Honesty notes

* The finding is bounded-time (≤ ~18 s per event) and requires either an
  evil/updated device or an unplug racing a tty open/close — Low severity,
  same weight class as DF-5359 (uipaq explore stall, 12.8 s, Low).
* What was executed: production `usb_process`/`usb_proc` drain path with
  ufoma's exact wait shape (2 runs). What was traced but not executed: the
  device-side trigger (MCPC descriptor + ACK withholding + forced
  disconnect) — every link is cited in ufoma.c / usb_serial.c /
  usb_process.c and is deterministic scheduling, not speculation.

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