# DF-4235 VERDICT — per-frame rl_init() on RX errors (remote DoS amplifier)

**Verdict: source-certain (Low); runtime inconclusive — QEMU's rtl8139
model accepts every frame with ROK set (no CRC/runt error injection), so
the error branch cannot be staged on the audit guest.**

## Trace

```
rl_intr()                     if_rl.c:1242
  :1270  if (status & RL_ISR_RX_ERR) rl_rxeof(sc);
rl_rxeof()                    if_rl.c:979
  :1028  if ((rxstat & RL_RXSTAT_RXOK) == 0) {
  :1029      IFNET_STAT_INC(ifp, ierrors, 1);
  :1030      rl_init(sc);        <-- FULL re-init, in intr ctx, serializer held
  :1031      return;
```

`rl_init()` (`if_rl.c:1388-1491`) cost per bad frame:
- `rl_stop()` (`:1400` → `:1580-1607`): chip quiesce + free all pending TX
  mbufs (drops co-queued frames),
- `CSR_WRITE_STREAM_4` MAC reprogram, `rl_list_tx_init`, RX/TX config,
- `rl_setmulti()` (`:1458` → `:640-687`): multicast TAILQ walk + per-entry
  `ether_crc32_be`,
- `mii_mediachg()` (`:1483`): PHY reprogram — on 8129 parts via
  `rl_mii_writereg` MDIO bit-banging with dozens of `DELAY(1)` PIO cycles
  (`if_rl.c:483-518`),
- `callout_reset` (`:1490`).

All of it inside the interrupt handler (or ifpoll compat) while holding
`ifp->if_serializer`. A remote sender emitting corrupted frames (bad CRC —
trivially crafted on the wire) at line rate keeps the adapter in a
permanent re-init cycle: every in-flight TX queue is flushed per frame, the
serializer is held for the whole reprogram, and the effective throughput of
the box on that serializer domain collapses. `RL_ISR_SYSTEM_ERR` frames
(`if_rl.c:1276-1279`) add `rl_reset()` — a busy-wait of up to
`RL_TIMEOUT × DELAY(10)` = 10 ms (`if_rl.c:696-700`, `if_rlreg.h:407`).

Per the audit rubric this is Low: single-NIC availability, needs a peer on
the wire, no memory-safety impact.

## Why inconclusive at runtime

The audit guest attaches a QEMU-emulated rtl8139 (see `guest_env.txt`:
`rl0@pci0:0:4:0 chip=0x813910ec`), and `rl_rxeof` was exercised live over
~1250 frames — but QEMU's model computes no CRC and pads/strips rather than
erroring, so `RL_RXSTAT_RXOK` is always set on delivery and the `:1028`
branch is unreachable from the guest. No `hw.rl` sysctl or register poke is
available to force it either. Honest classification: inconclusive (impact
demonstrable only on real hardware or a fault-injecting model).

## Adjacent negative results established during this live session

- `rl_rxeof` length trust is *not* a memory-safety bug on DFly: `m_devget()`
  (`sys/kern/uipc_mbuf.c:2235`) chunks arbitrary `len` into cluster-sized
  bcopy pieces (no mbuf overrun possible), `cur_rx` is `uint16_t` and
  `RL_RXBUFLEN` is `1<<16` (`if_rlreg.h:300-301`) so the missing `%`
  wraps exactly on ring size, and the `rx_bytes > max_bytes` window plus the
  wrap-branch `m_copyback` from `rl_rx_buf` all stay inside the 64 KB ring
  even for a lying length field.
- TX side: `ether_ioctl` caps `SIOCSIFMTU` at `ETHERMTU`
  (`sys/net/if_ethersubr.c:715-723`), `m_defrag` produces ≤ MCLBYTES
  clusters, and the 1-segment `bus_dmamap_load_mbuf_segment` fails cleanly
  otherwise — the 4-slot `rl_tx_chain` is always indexed mod 4 (`RL_INC`).
- eeprom/93cx6 and MII walks are fixed-size; `rl_setmulti` hashes are
  masked; no ioctl copies unvalidated user lengths (`rl_ioctl` delegates to
  `ifmedia_ioctl`/`ether_ioctl`).

## Fix

`fix.diff` — count-and-skip instead of re-init for ordinary frame errors.

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