# DF-5382 — umcs7840 intr-callback synchronous EP0 reads freeze the shared per-bus USB callback thread (evil MCS7840 device, indefinite bus-wide USB stall)

## What (finding in one line)

`umcs7840_intr_callback()` (sys/bus/u4b/serial/umcs.c:729-790) issues
**synchronous 500 ms-timeout control requests from the xfer-callback
context** — for every port whose interrupt byte signals
RXERR/RXHASDATA/RXTIMEOUT it reads LSR (umcs.c:756) and for MSCHANGE it
reads MSR (umcs.c:766). Xfer callbacks for the whole bus run serialized on
ONE kernel thread, `bus->non_giant_callback_proc`
(usb_bus.h:62,72; usb_transfer.c:1009-1014, 2286-2306, 2437), and each
request costs `UMCS7840_CTRL_TIMEOUT` = 500 ms (umcs.h:33, applied at
usb_request.c:421-428,557, slept at usb_request.c:642-644) plus a further
`hz/16` = 62.5 ms error pause (usb_request.c:735-745). A failed read only
breaks the inner switch (umcs.c:757 `/* Inner switch */`), so a 4-port
device pays **4 × 562.5 = 2250 ms of bus-thread occupancy per 13-byte
interrupt packet** — and an evil device streams packets (the pipe is
resubmitted at umcs.c:776-780), freezing every other USB device's transfer
callbacks on the bus (keyboard, mass storage, network) indefinitely.

Crafted packet: all four port bytes `0x00` —
`0x00 & MCS7840_UART_ISR_NOPENDING(0x01) == 0` → not skipped, and
`0x00 & MCS7840_UART_ISR_INTMASK(0x3f) == MCS7840_UART_ISR_MSCHANGE(0x00)`.

## Why this guest can't run the full path (evidence class)

No QEMU USB device model for the MosChip MCS7840 exists on the audit guest
(same situation as uipaq DF-5359 / ufoma DF-5367). Sanctioned evidence
class: **KLD harness compiled from production code** — it drives the real
in-kernel `usb_process` machinery (`usb_proc_create` / `usb_proc_msignal`,
usb_process.c) that `usb_callback_proc` uses to serialize xfer callbacks,
with the per-request wait durations taken verbatim from production
constants (500 ms timeout, hz/16 error pause, 4 ports). The deferred-status
fix mechanism of `fix.diff` is exercised the same way (Phase B).

## Build

    # on the guest (DragonFly 6.5-DEVELOPMENT, /usr/src present):
    mkdir -p /usr/src/sys/bus/u4b/serial/hx5382
    cp umcs_busfreeze_harness.c Makefile /usr/src/sys/bus/u4b/serial/hx5382/
    sh build.sh          # in-tree kmod build, -Werror clean

## Run

    sh run.sh            # kldload ./umcsbusfreeze.ko; kldunload; dmesg

## Expected output (decisive lines)

    HX5382[A]: victim delay after 1 packet = 2226 ms (expect ~2250 baseline / ~0 fixed)
    HX5382[A]: stream round 1: victim delay = 2169 ms
    HX5382[A]: stream round 2: victim delay = 2158 ms
    HX5382[A]: stream round 3: victim delay = 2159 ms
    HX5382[B]: victim delay after 1 packet = 0 ms
    HX5382[B]: stream round 1: victim delay = 0 ms
    ...

Phase A (baseline) MUST show the "other device's transfer" callback delayed
~2.2 s per crafted interrupt packet (repeatable every packet = sustained
bus freeze). Phase B (fix mechanism from fix.diff) MUST show ~0 ms.
Three module loads in run.log confirm stability: 2226/2240/2230-style
single-packet delays and 2152-2168 ms stream rounds, all fixed rounds 0 ms.

## Fix validation

* `fix.diff` — git-apply-able against `sys/` (`git apply --check` passes);
  the patched `umcs.c` compiles `-Werror`-clean as a kmod on the guest
  (`fix_compile.log`).
* Harness Phase B runs the deferred-status pattern against the same
  production machinery: victim delay 2250-class → **0 ms**.

## Artifacts

* `umcs_busfreeze_harness.c` — the KLD harness (annotated with the umcs.c /
  usb_transfer.c / usb_request.c lines it mirrors)
* `Makefile`, `build.sh`, `run.sh`
* `build.log`, `fix_compile.log`, `run.log` (decisive run, 3 loads),
  `run.2.log` (earlier stability run), `env.txt`
* `fix.diff` — defer intr-path LSR/MSR refresh to the ucom super taskqueue
* `VERDICT.md`, `manifest.json`, `verdict.json`
