# DF-3668 — VERDICT

**Status: inconclusive (runtime) / source-level certain. Impact class: interrupt-context soft-lockup DoS (INVARIANTS: panic). Hardware-gated — no ixgbe NIC on the guest.**

## Root cause

`ix_txeof()` (sys/dev/netif/ix/if_ix.c:2355-2387) is the TX completion path fed
directly by hardware-provided head indices:

```c
first = txr->tx_next_clean;          /* if_ix.c:2363 — software, ring-bounded */
if (first == hdr)
        return;
avail = txr->tx_avail;
while (first != hdr) {               /* if_ix.c:2368 */
        ...
        KKASSERT(avail < txr->tx_ndesc);   /* 2371: INVARIANTS only */
        ++avail;                          /* unbounded on bogus hdr */
        ...
        if (++first == txr->tx_ndesc)
                first = 0;                /* first wraps inside [0, ndesc) */
}
```

`hdr` arrives from:

- `*(txr->tx_hdr)` — the TX head **write-back buffer**, a cacheline of DMA
  memory the NIC writes (`if_ix.h:223`, mapped at `if_ix.c:2192-2195`):
  - legacy interrupt: `ix_intr` → `if_ix.c:1203`
  - MSI-X TX vector: `ix_msix_tx` → `if_ix.c:4788`
  - MSI-X combined RX/TX: `ix_msix_rxtx` → `if_ix.c:4810`
  - polling: `ix_npoll_tx` → `if_ix.c:4957`
- `IXGBE_READ_REG(hw, IXGBE_TDH(idx))` in `ix_txgc()` → `if_ix.c:2400`, same
  unbounded `while (first != hdr)` loop at 2409.

`hdr` is an `int` cast of a full 32-bit DMA'd word. The loop's exit condition
assumes `0 <= hdr < tx_ndesc`. A hostile or hung NIC that reports e.g.
`0xFFFFFFFF` (a plausible stuck-bus / reset-during-DMA artifact, or an
attacker-controlled DMA write) makes the condition unreachable:

- INVARIANTS kernels: `KKASSERT(avail < txr->tx_ndesc)` fires on the first
  wrap → **kernel panic**.
- production kernels: the loop never exits — the interrupt/polling thread
  spins in ISR context at 100% → **soft lockup / permanent DoS of the box**
  (RX on that vector stops, watchdog cannot run); `avail` (int) eventually
  overflows (UB) after 2^31 iterations.

There is no masking/clamping of the head anywhere in this driver — compare the
RX path which at least wraps `i` against `rx_ndesc` (if_ix.c:3085).

## Threat model

Requires control of (or a fault in) the NIC's descriptor write-back — the same
malicious-peripheral / broken-FWIW trust boundary as DF-3667. Not triggerable
by network packets alone. DoS-only: no memory is corrupted (`tx_buf[first]`
stays inside the ring because `first` wraps).

## Verification performed

1. Full source read of ix_txeof/ix_txgc/ix_msix_* / ix_intr (cited lines).
2. `env.txt`: no Intel 10GbE PCI device on the audit guest; `ix(4)` compiled
   into the kernel but never attached → runtime reproduction impossible.
3. `fix.diff` (reject `hdr` outside `[0, tx_ndesc)`) applied to the guest's
   md5-identical `/usr/src`; `if_ix.ko` rebuilt with `-Werror`, zero errors
   (`build_patched.log`). Local `sys/` untouched.

## Recommended fix

`fix.diff`: early-return on `hdr < 0 || hdr >= txr->tx_ndesc` in both
`ix_txeof()` and `ix_txgc()` (treat as spurious interrupt; the watchdog/txgc
path will re-run).

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