# DF-5515 VERDICT — reproduced (mechanism level, production code)

## Finding

`ubser_cfg_set_break()` (ubser.c:462-487) is the ONLY cfg-path request issuer
in the ubser driver: one synchronous EP0 vendor request
(`ucom_cfg_do_request` -> `usbd_do_request_proc`, usb_serial.h:220-221 /
usb_request.c:760-790) with a **1000 ms timeout** (ubser.c:481) whose error
return is **discarded** (ubser.c:482-485, DPRINTFN only). It runs on the
per-adapter ucom USB process thread (usb_serial.c:322) which executes all cfg
tasks under `sc_lock` (usb_process.c:94,152).

Production request accounting (verified against the core):
- first open(2) issues **0** EP0 requests (no ucom_cfg_open/close/param
  callbacks registered, ubser.c:175-184; the open-time line-state task computes
  break-off -> `set_break(sc,0)` -> gated by `if (onoff)`, ubser.c:470);
- TIOCSBRK (usb_serial.c:1190-1193 -> ucom_cfg_line_state, usb_serial.c:
  1415-1417) issues **1 x 1000 ms** request;
- TIOCCBRK issues **0** requests (the gate — DF-5517);
- close(2)/unplug cost is purely queue wait: `usb_proc_mwait` is an
  uninterruptible wait (usb_serial.c:671-672; usb_process.c:364-393) and
  `usb_proc_drain` on detach waits the same queue (usb_serial.c:394).

## Reproduction (KLD harness, production code included)

The harness includes the **unmodified** production `ubser.c` (newbus
registration neutered; md5-gated by build.sh) and runs the **real** static
`ubser_cfg_set_break()` on a **real** `usb_process` taskqueue
(`usb_proc_create` with the production lock protocol, usb_serial.c:322).
The modeled EP0 sink reproduces the production requester's stall: it is
called with the taskqueue lock held and sleeps the caller-supplied timeout
("WXFER", usb_request.c:643 style) on a unique wchan, returning
USB_ERR_TIMEOUT (silent device) or success at timeout-100 ms (slow-answer
device) — both of which production ubser discards.

Measured (run.log / run.2.log, hz=100):

| phase | scenario | measured |
|---|---|---|
| A | open + TIOCSBRK + close, silent device | request 212->1215 ms; **closer blocked 914 ms** uninterruptibly behind it; 1 request |
| B | 5 SBRK/CBRK pairs paced 1.1 s (sustained loop) | **5 requests, taskqueue continuously occupied 205->5933 ms (~5.7 s)**; extends without bound while the loop runs |
| B2 | slow-answer device (request SUCCEEDS) | closer blocked **818 ms** (799 ms on rerun) with **no error visible anywhere** — the invisible variant |
| C (fixed) | timeout 250 ms (fix.diff) | closer blocked **91 ms** — stall GONE |

The close(2) block is uninterruptible (usb_proc_mwait cv_wait,
usb_process.c:364-393 — no PCATCH/signal check), and unplug drains the same
queue on the USB hub event thread (usb_serial.c:394), so an evil BWCT adapter
stalls both a closing process and device removal by the same mechanism.

Local amplification is bounded by device node permissions (ttyU* 0600
root:wheel, cuaU* 0660 uucp:dialer, usb_serial.c:477-488): an unprivileged
user outside group dialer cannot open the tty to issue TIOCSBRK. The evil
device itself needs no privilege at all.

## Fix validation

fix.diff (ubser.c:481: timeout 1000 -> 250 ms) applied to the guest's
/usr/src copy (guest patch(1), hunk #1 @477); harness rebuilt -Werror clean;
rerun: phase A closer ELAPSED **91 ms** vs 914 ms baseline (run.fixed.log).
Guest tree restored and md5-verified (85afa08fa4f618c9288a6dca75d9e671).
`git apply --check` passes against the audited host repo.

## Impact

Availability only (per-adapter taskqueue pin 1 s per TIOCSBRK, uninterruptible
close(2), delayed unplug; sustained break loop = unbounded occupancy).
Low per family calibration — this is the family minimum: exactly ONE
request, and only on TIOCSBRK.

## Honest scope notes

- Mechanism-level: no physical BWCT adapter exists for the guest; the EP0
  sink timing (full-timeout NAK / just-under-timeout answer) is modeled, the
  taskqueue, lock protocol, driver callback and request parameters are
  production.
- fix_build.log holds the final link lines of the fixed build only (the
  baseline build.log is full).

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