# DF-3667 — VERDICT

**Status: inconclusive (runtime) / source-level certain. Impact class: kernel info leak (heap OOB read) via malicious-NIC DMA. No guest reproduction possible — no ixgbe hardware.**

## Root cause (path:line into the audited tree)

`ix_rxeof()` reads the NIC-written RX completion descriptor and publishes its
16-bit length into the mbuf with **no bound against the cluster actually posted
for that descriptor**:

- `sys/dev/netif/ix/if_ix.c:2974` — `len = le16toh(cur->wb.upper.length);`
  (`wb.upper.length` is DMA'd by the NIC into the descriptor ring.)
- `sys/dev/netif/ix/if_ix.c:3031` — `mp->m_len = len;`
- `sys/dev/netif/ix/if_ix.c:3040` — `mp->m_pkthdr.len = len;`
- `sys/dev/netif/ix/if_ix.c:3044` — `rxbuf->fmp->m_pkthdr.len += len;` (chain)

The backing cluster is allocated at:

- `sys/dev/netif/ix/if_ix.c:4142` — `m_getjcl(flags, MT_DATA, M_PKTHDR, rxr->rx_mbuf_sz);`
- `sys/dev/netif/ix/if_ix.c:2635-2638` — `rx_mbuf_sz = MCLBYTES (2048)` when
  `max_frame_size <= MCLBYTES`, else `MJUMPAGESIZE (4096)`.
  Note: SIOCSIFMTU allows MTU up to `IX_MAX_MTU = 9710` (if_ix.h:101), so 4096
  is the *largest* cluster ever posted — there is no MJUM9BYTES/MJUM16BYTES
  branch in this driver.

There is **no `len > rx_mbuf_sz` (or `len - ETHER_CRC_LEN`) guard anywhere in
ix_rxeof** — contrast the bge/bnx/bce family fixes from this same audit
(DF-3663:3138, DF-3639:2848, DF-3604:4396).

## Threat model

The descriptor write-back is produced by the NIC via DMA. A compromised or
malicious peripheral (Thunderclap-style DMA attack, malicious FW/NVM on the
card, or a buggy NIC erratum writing an inconsistent `wb.upper.length`) fully
controls `len`. Nothing on the host validates it. `mp->m_len = len` then hands
the network stack an mbuf claiming up to 65535 valid bytes inside a 2048/4096
byte cluster; every downstream consumer (ether_demux, ip_input, BPF taps,
forwarding, `m_copydata`-style parsers) reads kernel heap beyond the cluster:
**up to 63,487 bytes of adjacent kernel heap per descriptor** are exposed to
parsing/leak paths, and `m_pkthdr.len` accumulation (3044) compounds it for
multi-descriptor frames.

This is not remotely triggerable by a *packet* alone — the NIC must misreport
the length; the NIC is the trust boundary being violated. Same treatment as the
bge/bnx/bce family findings (Medium, certain, kernleak).

## What was verified, and how

1. **Source-level certainty:** full read of `ix_rxeof()` (if_ix.c:2948-3100) —
   no bound exists between the length read (2974) and the mbuf publication
   (3031/3040/3044). Cluster sizing confirmed at 2635-2638/4142 and
   `sys/sys/param.h:473,497` (MCLBYTES=2048, MJUMPAGESIZE=4096).
2. **Bound-math demonstration (userspace, guest):** `df3667_sim.c` replicated
   the assignment chain with poisoned redzones; ran on the audit QEMU guest
   (`run.log`): `desc len=0xffff -> m_len=65535` in a 2048 cluster ⇒
   63,487-byte OOB span. This is arithmetic evidence only — the kernel path
   itself did not execute.
3. **Module presence:** ix(4) is compiled into the running kernel
   (X86_64_GENERIC line 217, `env.txt`), so the vulnerable code ships in the
   default kernel of this system; no Intel 10GbE PCI device exists on the guest
   (`pciconf -l`, `dev.ix.*` count 0), so `ix_attach`/`ix_rxeof` never runs.
4. **Fix validation (compile-level):** `fix.diff` adds
   `if (len > rxr->rx_mbuf_sz) { ierrors++; ix_rx_discard(...); goto next_desc; }`
   after `eop` is computed. Applied to the guest's `/usr/src` copy (md5-identical
   to the audit tree) → `if_ix.ko` rebuilt with `-Werror`, zero errors/warnings
   (`build_patched.log` vs `build_baseline.log`). The local `sys/` tree was
   never modified. An earlier draft of the hunk placed the check before `eop`
   is computed and the `-Werror` build caught it (`maybe-uninitialized`) —
   final hunk placement is after the `--count` line.

## Why not reproduced / why no exploit chain

Escalation to `uid=0` requires the memory-corruption primitive to be exercisable;
exercising it requires a DMA-capable adversary on the PCIe bus (or a NIC whose
firmware misreports lengths). The guest has no such device and QEMU (TCG/KVM
q35) has no 82599/X540 model to instantiate, so neither a leak nor a panic can
be triggered at runtime. Verdict is therefore `inconclusive` per the honest
status taxonomy (device absent), with `confidence: certain` on the source-level
defect and its impact class.

## Recommended fix

`fix.diff` in this pack (bound the descriptor length to `rx_mbuf_sz`, discard
the frame on violation). Mirrors the fixes already carried for bge/bnx/bce in
this audit.

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