# VERDICT — DF-4056 (unbounded rxeof/txeof walks; device indices wider than the SW rings)

**Status: reproduced — both halves** (production-handler KLD dispatch,
DF-4106 vge / DF-4266 wb evidence class) — **and fix validated** on a
rebuilt kernel.

## Geometry (headers, confirmed in the running kernel's disassembly)

- `ET_RXS_STATRING_INDEX` = `__BITS(27,16)` — **12-bit** device field
  (if_etvar.h:107); the software stat ring is `ET_RX_NSTAT` = 1024
  entries (if_etvar.h:45). Values 1024..4095 can never equal
  `rsr_index`.
- `ET_TX_DONE_POS_INDEX` = `__BITS(9,0)` — **10-bit** device field
  (if_etreg.h:139); the TX ring is `ET_TX_NDESC` = 512 (if_etvar.h:42).
  Values 512..1023 can never equal `tbd_start_index`.

## TX half — KKASSERT panic (panic_tx.txt)

Dispatched the **real `et_txeof(sc, 0)`** (the `et_start`:1223 call
shape; symbol `t et_txeof`) with `tbd_used=1` and the MMIO stand-in's
`ET_TX_DONE_POS` = index 600, wrap 0:

```
panic: assertion "tbd->tbd_used > 0" failed in et_txeof at
/usr/src/sys/dev/netif/et/if_et.c:2050
et_txeof() at et_txeof+0x1cb 0xffffffff804b690b
```

Exactly as predicted: the loop predicate at :2029 can never be
satisfied, the walk decrements `tbd_used` past zero, the KKASSERT at
:2050 fires. Guest died at the ddb prompt.

## RX half — livelock under the serializer (run_rx_livelock.log)

Dispatched the **real `et_intr`** (inlined `et_rxeof`) under the rig's
serializer with `rxs_stat_ring` = STATRING index 2000:

- the completion walk (if_et.c:1851) never terminates:
  ierrors 4,230,460,413 → 4,253,528,605 in ~2 s (**~11.5M
  iterations/s**), still growing linearly 9 minutes later
  (6.6e9+ → +11.7M/s) — the walk sprays `rbd_newbuf` reallocations,
  mbuf frees and CSR writes while the interface serializer stays held;
- serializer capture proven: a second thread's
  `lwkt_serialize_enter` on the same serializer blocked forever
  (`kern.dfet.grab` never returned; zero `ACQUIRED` lines) — in
  production every later ioctl/if_start on the interface blocks;
- one CPU pegged, caller unkillable, `ps -ax` itself hangs, and the
  guest could not be shut down cleanly afterwards (host force-killed
  QEMU) — same shutdown wedge as the DF-3715/4025/4046/4051 family.

## Rig iteration notes

- The first S1 run (DF-4055 pack) also panicked at `m_free` — sink
  adjusted to bound the demonstration; unrelated to this finding.
- S3 launched via a backgrounded sysctl; observation from a second
  session (stats/grab sysctls); `ps` replaced by sysctl probes after
  it proved to hang while the spinner runs.

## Fix validation (kernel #1, Oct 14 2026, same tree + fix.diff)

fix.diff validates both device indices against the ring geometry
before the walks (`rxst_index >= ET_RX_NSTAT` → ierrors + return;
`end >= ET_TX_NDESC` → oerrors + return). Identical rig on the
rebuilt kernel:

| scenario | baseline (#0) | fixed (#1) |
|---|---|---|
| TX: DONE index 600 | panic "tbd_used > 0" (if_et.c:2050) | `dfet0: invalid TX done index 600`, `et_txeof RETURNED (tbd_used=1)`, no panic |
| RX: STATRING index 2000 | livelock, ~11.5M iter/s forever, serializer held | `dfet0: invalid RX stat ring index 2000`, `et_intr RETURNED (ierrors=4)`, no livelock |

fix_status: **fixed** (baseline_reproduced=1, patched_reproduced=0).

## Impact

One device register/DMA write by a faulty or hostile NIC, then every
TX interrupt panics the kernel (stock KKASSERT) or every RX interrupt
permanently livelocks a CPU with the interface serializer held. Both
observed deterministically. Severity Medium stands (hardware-mediated
trigger).

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