Unvalidated vf index in exported MDD/anti-spoof ops: register-index OOB MMIO read-modify-write if wired with vf beyond the register window
Summary
ixgbe_set_ethertype_anti_spoofing_X550 computes IXGBE_PFVFSPOOF(vf>>3) with no vf bound (8 registers exist - vf>=64 exits the block), and ixgbe_restore_mdd_vf_X550 feeds WQBR_TX/RX(vf*num_qs/32) with unbounded vf (4 registers). The sibling pruning op DOES bound its index, proving the omission is an oversight. Latent: if_ix.c never dispatches these ops. A future kernel-internal VF loop would have to receive an attacker-influenced index for this to become an OOB MMIO write of a small bitmask. fix.diff compile-validated.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3726 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.5 KB | β raw | |
| VERDICT.md | β | 2.6 KB | β raw | |
| fix.diff | β | 919 B | view raw | |
| manifest.json | β | 717 B | view raw | |
| verdict.json | β | 1.8 KB | view raw |
DF-3726 β Unvalidated vf index in exported MDD/anti-spoof ops: register-index OOB MMIO write if wired with vf β₯ pool count
File: sys/dev/netif/ix/ixgbe_x550.c:1075-1091, 1406-1437, 1446-1494
Severity: Info (latent hardening β no in-tree caller) Confidence: certain
ixgbe_set_ethertype_anti_spoofing_X550(:1078vf_target_reg = vf >> 3) computesIXGBE_PFVFSPOOF(vf_target_reg)with no bound onvf: the PFVFSPOOF block has exactly 8 registers (ixgbe_type.h:534,557) β anyvf >= 64reads+writes MMIO past the block.ixgbe_restore_mdd_vf_X550(:1429start_q = vf * num_qs, :1432idx = start_q / 32) feedsIXGBE_WQBR_TX/RX(idx)(4 registers each, ixgbe_type.h:472-473) with an unboundedvfβvf >= 128in the 8-queue MRQC mode already yields idx β₯ 32 β far out of the WQBR window.ixgbe_mdd_event_X550(:1489-1490) writesvf_bitmap[vf/32]; vf β€ 63 by construction here (q β€ 127, shift β₯ 1), so it needs a 2-word bitmap β an undocumented caller contract.
Asymmetry proof of intent: the sibling
ixgbe_set_source_address_pruning_X550 does bound its index
(if (pool > 63) return; at :1052-1054).
Dead in-tree: if_ix.c never calls ixgbe_set_ethertype_anti_spoofing,
ixgbe_mdd_event, or ixgbe_restore_mdd_vf (no SR-IOV VF-management wiring);
ops are exported HAL surface for future callers. Upstream-identical (FreeBSD
head verified). fix.diff adds the two missing bounds checks
(compile-validated, DF-3726_make_rc=0, see ../DF-3724/fix_build.log).
DF-3726 β VERDICT
Status: inconclusive at runtime (no X550 hardware; no in-tree caller). Source-certain missing input validation on exported HAL ops.
Root cause
Three X550 mac-ops consume a VF/pool index with no range validation:
- ixgbe_x550.c:1078 β
int vf_target_reg = vf >> 3;β¦ :1084/:1090IXGBE_READ_REG/WRITE_REG(hw, IXGBE_PFVFSPOOF(vf_target_reg), β¦).IXGBE_PFVFSPOOF(_i)(ixgbe_type.h:534) is0x08200 + 4*iwith exactly 8 registers (IXGBE_PFVFSPOOF_REG_COUNT, ixgbe_type.h:557).vf >= 64(or negative) indexes outside the block β OOB MMIO read-modify-write. - ixgbe_x550.c:1429-1436 β
start_q = vf * num_qs(num_qs from the live MRQC mode, 2/4/8),idx = start_q / 32, thenIXGBE_WRITE_REG(hw, IXGBE_WQBR_TX(idx) / IXGBE_WQBR_RX(idx), reg). WQBR windows are 4 registers (ixgbe_type.h:472-473) covering 128 queues;vfbeyond the mode's pool count walks out of the window. - ixgbe_x550.c:1489-1490 β
idx = vf / 32; vf_bitmap[idx] |= β¦with vf β€ 63 by construction (q β€ 127 from 4Γ32 WQBR bits, shift β₯ 1) β requires a β₯ 2-word bitmap; contract undocumented in ixgbe_api.c:1289.
The pruning sibling bounds its index (pool > 63, :1052-1054) β the two
missing checks are an oversight, not a design choice.
Why Info
- No in-tree caller:
rgover if_ix.c shows the driver never dispatches these ops (they belong to SR-IOV/VF-management paths this driver does not implement); ixgbe_api.c wrappers (:1223, :1289, :1302) are the only (unused) entry points. Compiled-in, never executed β latent. - When eventually wired, callers are kernel-internal VF loops; the index would have to come from a future ioctl/sysctl to be attacker-influenced.
- Upstream-identical (FreeBSD head
ixgbe_restore_mdd_vf_X550has no bound either).
Verification
- Guest has no X550 NIC (../DF-3723/env.txt) β cannot execute.
fix.diff(vf bounds in both functions) compile-validated on guest/usr/srcwith-Werror(DF-3726_make_rc=0, ../DF-3724/fix_build.log), reverted after (md5 restored).
Recommended fix
fix.diff: reject vf < 0 || vf >= IXGBE_PFVFSPOOF_REG_COUNT * 8 in the
anti-spoof op and vf >= 64 in restore_mdd_vf; document the 2-word bitmap
contract for mdd_event.
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.
Fix verification
inconclusivefix.diff applied to guest /usr/src; module rebuild clean under -Werror (DF-3726_make_rc=0); reverted. Runtime validation impossible (no X550 hardware, no caller).
../DF-3724/fix_build.log: DF-3726_make_rc=0 + md5 restored
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md path:line trace; fix compile-validated DF-3726_make_rc=0 in ../DF-3724/fix_build.log; ../DF-3723/env.txt (no X550 on guest)
PoC changes
n/a (source-level)
Verified recommended fix
Bound vf (>= IXGBE_PFVFSPOOF_REG_COUNT*8 reject; >= 64 reject in restore_mdd_vf) β fix.diff
Verdict
ixgbe_set_ethertype_anti_spoofing_X550 and ixgbe_restore_mdd_vf_X550 consume their vf index with no range validation (ixgbe_x550.c:1078, :1429-1436): vf >= 64 (PFVFSPOOF, 8 regs) or vf beyond the MRQC pool count (WQBR, 4 regs) would index outside the register windows β OOB MMIO RMW. Latent: if_ix.c never dispatches these ops (no SR-IOV wiring) and the sibling pruning op does bound its index (:1052-1054), proving intent. Upstream-identical (FreeBSD head). mdd_event's 2-word vf_bitmap contract (:1489-1490) is undocumented.
No comments yet.