# DF-4304 VERDICT — unbounded, range-unvalidated RX/TX completion walks in bfe

## Bottom line

**Source-certain invariant break; runtime honestly inconclusive**
(hardware-gated: the guest has no BCM4401/BCM4402 and QEMU cannot emulate
one — `NO_BROADCOM_PCI`, `sysctl hw.bfe` unknown oid; guest src md5 ==
audited tree). Availability-only defect (interrupt-context livelock /
KKASSERT panic); no memory corruption — software indices never leave
0..510, so no OOB access occurs; the failure mode is non-termination.

## The math (all constants from sys/dev/netif/bfe/if_bfereg.h)

- `BFE_STAT_CDMASK = 0x00000fff` (:69) — 12-bit "current descriptor" BYTE
  offset in DMARX_STAT (:91) / DMATX_STAT (:68).
- `sizeof(struct bfe_desc) = 8` (:434-437, two uint32_t).
- Decoded index range: `0..4095/8` = **0..511**.
- `BFE_RX_LIST_CNT = BFE_TX_LIST_CNT = 511` (:405-406) — valid indices
  **0..510**; desc 510 carries `BFE_DESC_EOT` (if_bfe.c:619-620).
- `BFE_INC(x, 511)` (:426) cycles software indices over 0..510 — it can
  never produce 511.

⇒ `while (current != cons)` (bfe_rxeof :1109) and `while (i != chipidx)`
(bfe_txeof :1070) cannot terminate when the register reports an offset in
[4088..4095]. Both decode sites (`:1064-1065`, `:1106-1107`) perform no
range validation, and neither loop has an iteration bound.

## Why the off-range value is expressible

- The field's full legal encoding (12 bits) includes index 511 — this is
  not a "garbage read" argument, it is the register's own value space.
- The driver itself writes the byte offset 4088 (= 511*8) into the SIBLING
  register BFE_DMARX_PTR at if_bfe.c:549 — "posted through end of ring" is
  an architecturally used value on this DMA engine.
- Nominal operation keeps DMARX_STAT ≤ 4087 (EOT wrap at desc 510), but
  nothing in the driver contract guarantees the post-EOT transient
  (increment-before-wrap), error-state reporting, or single-bit corruption
  stays ≤ 4087 — and the driver's own error handling (ISTAT_ERRORS →
  bfe_init, if_bfe.c:1176-1187) runs BEFORE bfe_rxeof (:1190-1191), so it
  cannot rescue a walk that starts with a bogus index.

## Impact if triggered (real bfe(4) hardware)

- rxeof variant: interrupt thread spins forever holding the ifnet
  serializer; each iteration does m_getcl + bus_dmamap_load + sync +
  header read (len underflow → ierrors branch, bfe_setup_rxdesc) — a full
  mbuf-allocation storm at interrupt priority; one CPU pegged; interface
  and all its ioctls/timers wedge; system-wide degradation.
- txeof variant: same spin; alternatively `KKASSERT(sc->bfe_tx_cnt > 0)`
  (:1080) panics first if the phantom walk crosses an empty ring.

## Harness (guest, run.log)

`bfe_walk_harness.c` enumerates the encodable index space and simulates
the BFE_INC walk: index 511 ⇒ "walk NEVER TERMINATES"; all indices ≤ 510
terminate. Compiled and executed on the guest (CC_RC=0, HARN_RC=0) — see
run.log.

## Fix

Range-validate the decoded index before walking (bfe_txeof and bfe_rxeof)
— `if (chipidx >= BFE_TX_LIST_CNT) return;` with a rate-limited printf;
optionally add a work bound per interrupt. fix.diff implements the range
guards (`patch -p1 --dry-run` clean). Cannot be runtime-validated without
hardware → fix_status not_testable.

## Negative results (adjacent candidates killed)

- **rxeof length trust (checklist DF-3604 family): KILLED.**
  `len = rxheader->len - ETHER_CRC_LEN` (:1117) is uint16 arithmetic
  promoted then stored to uint32_t; underflow (len<4) yields a huge value
  caught by `len > ETHER_MAX_LEN + 32` (:1121) → error branch. Passing
  lens are ≤ 1550; cluster is MCLBYTES=2048 with BFE_RX_OFFSET=30
  headroom (:1139) ⇒ max touch 1580 < 2048. BFE_RX_FLAG_ERRORS
  (:390-391) covers OFIFO|CRCERR|SERR|ODD; LARGE frames are covered by
  the len bound. No cluster OOB is constructible.
- **tx ring invariant (checklist item b): KILLED for software.**
  bfe_encap (:1203-1268) bounds nsegs by maxsegs = 511 - tx_cnt - 2
  (:1209-1212), the start/oactive checks in bfe_start (:1298-1301) keep
  tx_cnt + SPARE < LIST_CNT, and the map-swap (:1257-1258) is the
  standard DFly first/last juggle. Software indices stay in 0..510.
- **eeprom/stat walks: bounded** (bfe_read_eeprom :960-962 fixed 128
  bytes; bfe_stats_update :1051-1055 fixed register ranges;
  bfe_clear_stats :672-675 fixed).

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