# DF-4236 VERDICT — churn-induced missed TX completions / watchdog storms on rl(4)

**Verdict: reproduced (dos, Low). Observed 2/2 churn sessions on the
QEMU-emulated rtl8139: recurring `rl0: watchdog timeout`, `Oerrs` climbing
(6→11), queries needing 4 s retransmits, one ~30 s hard TX stall that
self-healed via the watchdog-driven `rl_init()`. No panic, no memory
corruption, guest stayed up. Root-cause attribution driver-vs-QEMU-model is
*likely but not fully pinned* — the honest statement is "the shipped driver
+ this (widely used) device model enter a degraded state together"; the
same churn against real silicon is untested.**

## Observation

- Baseline (no churn): DNS over rl0 works, 0 oerrors, no watchdogs
  (~1250 packets over the session, incl. 1400-byte EDNS frames).
- 300 rapid `ifconfig rl0 down; up` cycles → `rl0: watchdog timeout`
  (5 messages, session 1; 5 more, session 2 — 11 total), `Oerrs`
  0→11, retransmit gaps of exactly 4 s (nslookup timeout), one ~30 s
  complete TX stall (session 1 final probe timed out 3×) that later
  self-healed without any action beyond further `rl_init()`s.
- `if_timer` is armed **only** by `rl_start()` (`if_rl.c:1375`) and rearmed
  by `rl_txeof()` while TX is pending (`if_rl.c:1151-1154`); therefore
  every watchdog observation proves frames were dequeued into the chip and
  their completion was never seen by `rl_txeof()`'s `RL_LAST_TXSTAT` test
  (`if_rl.c:1122-1125`) within 5 seconds.
- Recovery always occurred via `rl_watchdog()` → `rl_init()`
  (`if_rl.c:1570-1572`), i.e. the failure state is chip/driver state that a
  full register reprogram clears (note `rl_init()` does *not* issue
  `rl_reset()`).

## Attribution analysis (why "likely", not "certain")

Driver-side facts (`sys/dev/netif/rl/if_rl.c`):
- `rl_stop()` (`:1591-1592`) stops the engine with `RL_COMMAND=0`, `IMR=0`
  and `rl_list_tx_init()` (`:1418` → `:935-949`) resets `cur_tx/last_tx`
  and zeroes `RL_TXADDR0-3` — but **`RL_TXSTAT0-3` are never cleared**.
  Stale completion bits (TOK/OWN-threshold from the previous session, plus
  stale byte counts) survive every re-init.
- `rl_init()` does not call `rl_reset()`, so any internal descriptor/timer
  state inside the chip that keys off TxStatus is carried across the churn.
- `rl_txeof()` gates on `(txstat & (TX_OK|UNDERRUN|TXABRT)) == 0` — a
  descriptor the model believes "not yet complete" stalls the slot until
  the watchdog re-inits everything.
- With 4 slots and `RL_INC` mod-4 bookkeeping, a single missed completion
  wedges `cur_tx == last_tx` rotation for that slot pairing and every
  subsequent packet waits on it.

Emulator-side facts: QEMU's rtl8139 (non-C+ mode) transmits on the
TxStatus write and updates status registers from its model state; how it
reacts to `TxAddr=0` with stale `TxStatus` bits (exactly what
`rl_list_tx_init` produces mid-churn) is the kind of corner where a model
and a driver can desync. Real 8139 silicon is specified to trigger on the
driver's descriptor write, which makes the stale-status window mostly
benign on hardware — but that is exactly the untested half.

Given the trigger needs `ifconfig` (root), the audit rubric puts this at
Low. It is filed because (a) it is a *reproduced* availability defect on
the audit's own reference emulation, (b) the same re-init state machine
runs unprivileged-triggers-adjacent via `rl_watchdog()` and
`rl_intr(SYSTEM_ERR)`, and (c) it gives maintainers a concrete, cheap
experiment (clear TxStatus in `rl_list_tx_init`, or add `rl_reset()` on
re-init) — see fix.diff candidate.

## Fix validation

fix_status = not_testable: the candidate fix (clear `RL_TXSTAT0-3` in
`rl_list_tx_init`) is a one-line, build-safe change, but validating it
requires a kernel rebuild + re-churn cycle whose outcome cannot
distinguish "fix works" from "QEMU model happened to behave" without
hardware cross-checks; the audit contract's kernel-rebuild budget is
reserved for memory-corruption findings. Left as a recommended fix.

## Artifacts

run.log (both sessions, netstat/dmesg/tcpdump excerpts), rl_live_test.sh,
rl_repro.sh (in DF-4235/, shared session), guest_env.txt (attach proof,
final counters), dfbsd-qemu/vm_rl.sh (temp launcher).

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