# DF-5455 VERDICT — reproduced (impact: leak, 4 bits of kernel stack per status refresh)

## Finding

`umoscom_cfg_read()` (sys/bus/u4b/serial/umoscom.c:539-557) declares
`uint8_t val;` uninitialized, issues a 1-byte vendor control-IN through
`ucom_cfg_do_request()` (macro, usb_serial.h:220-221 →
`usbd_do_request_proc(..., actlen=NULL, 1000)`), and **returns `val`
without checking the usb_error_t result** — the request's return value is
discarded at both call sites (umoscom.c:551-556 writes nothing to `val` on
failure).

Core semantics traced in sys/bus/u4b/usb_request.c:

- **timeout / stall / cancel**: the WXFER wait loop breaks on
  `xfer->error` (usb_request.c:642-650) *before* the `usbd_copy_out()` at
  :680-681 — the caller's buffer is never written;
- **zero-length data stage, clean status stage**: SUCCESS return with
  `temp == 0`, nothing copied (usb_request.c:666-683);
- **short (0-of-1 byte) data stage**: umoscom passes `flags=0` (no
  USB_SHORT_XFER_OK) → `USB_ERR_SHORT_XFER` → same no-copy break;
- the macro hard-codes `actlen=NULL`, so the core's only zeroization hook
  (`*actlen = 0` at :448-449) does not apply to the buffer.

Consumer chain (this is what separates it from the consumer-less uvscom
sibling DF-5448):

```
umoscom_cfg_get_status()            umoscom.c:495-522
  msr = umoscom_cfg_read(UMOSCOM_MSR)         :507   <-- uninit byte
  *p_msr |= SER_CTS/DCD/RI/DSR from msr bits 0x10/0x80/0x40/0x20  :511-521
ucom_cfg_status_change()             usb_serial.c:1515-1518 (new_msr starts 0)
  sc->sc_msr = new_msr                        :1527
ucom_modem(tp,0,0) / TIOCMGET        usb_serial.c:1321-1332, 1209-1211
  ucom_totio(): SER_CTS→TIOCM_CTS, SER_DCD→TIOCM_CD,
                SER_RI→TIOCM_RI, SER_DSR→TIOCM_DSR            :1259-1270
  copyout of the int to userspace
```

⇒ **bits 4..7 of one uninitialized kernel-stack byte are disclosed to an
unprivileged TIOCMGET reader, 4 bits per status refresh**, whenever the
MSR vendor read fails (evil device: stay silent / stall / reply 0 bytes;
flaky device: timeout). Status refreshes are device-driven (interrupt
pipe → `ucom_status_change`, umoscom.c:681) and open-driven
(usb_serial.c:880), so an evil adapter controls the refresh rate.

## Reproduction (KLD harness with production code, guest 6.5-DEVELOPMENT)

- The guarded production copy (1 hunk, `prod_guard.diff`) compiles the
  real `umoscom_cfg_read`/`umoscom_cfg_get_status` verbatim; only the EP0
  transport is shimmed with the traced failure semantics.
- Disassembly of the built module (`umoscom_cfg_read.constprop.2`):
  `lea -0x9(%rbp),%rsi` (address of `val` handed to the opaque call),
  then `movzbl -0x9(%rbp),%eax` — a genuine uninitialized stack load —
  feeding `umoscom_cfg_get_status`'s four `test/orb` SER translations.
- Stack poisoned with known patterns right before each production call:
  **12/12 pattern-matches per load, 3 loads, 0 mismatches**
  (`run.log`): 0xff→SER 0xf0 (TIOCM 0741: CTS+DSR+RI+CD+LE), 0xa5→0xa0
  (0501), 0x5a→0x50 (0241), 0xf0/0x00/0x0f as predicted — for BOTH
  failure modes (timeout and zero-length success).
- Counterfactual with the one-line fix (`uint8_t val = 0;`), identical
  shim + poisoning: msr=0x00 in every case.

Two artifacts document the journey: `run.1.fold.log` (when the shim was
same-TU inlinable, GCC -O2 folded the provably-uninitialized load to a
constant 0 — the load never happened and results were all-zero; the shim
now lives in its own TU exactly like production's cross-module
`usbd_do_request_proc`) and `build.werror_evidence.log` (the
production-shaped cross-TU build produces **no diagnostic at all** —
`-Wuninitialized` only fires when GCC can see through the callee, which
is why this class ships).

## Impact ceiling

4 bits per refresh of *stale kernel stack* from the ucom task thread's
frame region (prior cfg tasks leave termios copies, request buffers,
pointers there). Not a classic address leak by itself (top bits of a
pointer are the usual take), low-bandwidth, evil-device or fault
precondition. Severity: **Low** (CWE-457/CWE-908, kernleak bucket),
consistent with family precedent (DF-1062, DF-2342) — but note this
instance has a live userspace consumer, unlike DF-5448.

## Fix validation

- `fix.diff` (init `val`): patched production `umoscom.ko` builds
  -Werror-clean on the guest (`fix_compile.log`, RC=0) — with
  -Wuninitialized armed, i.e. the same-TU diagnostic that fires on the
  unfixed code is gone.
- Runtime: the fixed twin path under identical poisoning reports zero
  SER bits (36/36 clean across 3 loads, `run.log`).

Not applied to `sys/` (read-only audit target).

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