# DF-5248 — `run_drain_fifo()` unbounded loop wedges the net80211 task and hangs detach

`sys/bus/u4b/wlan/if_run.c:2503-2563`

## Root cause (path:line)

```c
for (;;) {                                            /* 2513 */
    run_read(sc, RT2860_TX_STAT_FIFO, &stat);         /* 2515 - return ignored */
    if (!(stat & RT2860_TXQ_VLD))                     /* 2517 */
        break;
    wcid = (stat >> RT2860_TXQ_WCID_SHIFT) & 0xff;    /* 2520 */
    if (!(stat & RT2860_TXQ_ACKREQ) || wcid > RT2870_WCID_MAX ||
        wcid == 0)                                    /* 2523 */
        continue;                                     /* 2525 */
    ...
}
```

- `RT2860_TXQ_VLD == (1 << 0)` (if_runreg.h:664).
- `run_read()` on USB failure sets `*val = 0xffffffff`
  (if_run.c:1250-1255). `0xffffffff` has bit 0 set **and** `wcid == 0xff`
  (→ `wcid > RT2870_WCID_MAX`, 2523-2525 `continue`).

So every failing control read keeps the loop alive: the loop has **no
iteration bound and no error exit**. A device that stops answering
control transfers (unplugged mid-hostap-TX, wedged, or hostile
physical-USB device that simply stalls `RT2870_READ_REGION_1` or always
returns `VLD|ACKREQ` data) makes `run_drain_fifo()` spin forever. Each
`run_read()` takes up to 10 × 250 ms of retries in `run_do_request`
(if_run.c:1223-1242), so the loop is not even a cheap spin: the cmdq
task thread is lost forever.

Consequences:

1. `run_ratectl_cb()` → `run_drain_fifo()` (2490) — the ratectl task
   never returns; the 1 s callout state machine is dead.
2. `run_tx()` FIFO-drain deferral (3427-3429) queues onto the same
   wedged task.
3. `run_detach()` calls `ieee80211_draintask(ic, &sc->cmdq_task)` /
   `ratectl_task` (879-880) → `taskqueue_drain()` blocks forever →
   `device_detach` never returns → the USB exploration thread hangs;
   replugging the adapter is impossible without a reboot.
   (Persistent kernel DoS from a device *unplug at the wrong moment* or
   from a hostile device.)

## Simulated verification

`drain_fifo_loop.c` reproduces both non-termination conditions with the
exact kernel expressions; both scenarios run to the artificial
1,000,000-iteration cap without terminating (see run.log).

Dynamic in-kernel repro requires a run(4) device (hardware-gated on this
QEMU guest) → `inconclusive`, confidence `certain` on the logic
(the loop conditions are decidable statically and were executed
faithfully in the harness).

## Fix

Bound the loop (FIFO is 16 deep) and break on read error — see fix.diff
(applies cleanly with `git apply`).

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