β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-3726

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3726 Β· 5 files
FileTypeDescriptionSize
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 (:1078 vf_target_reg = vf >> 3) computes IXGBE_PFVFSPOOF(vf_target_reg) with no bound on vf: the PFVFSPOOF block has exactly 8 registers (ixgbe_type.h:534,557) β€” any vf >= 64 reads+writes MMIO past the block.
  • ixgbe_restore_mdd_vf_X550 (:1429 start_q = vf * num_qs, :1432 idx = start_q / 32) feeds IXGBE_WQBR_TX/RX(idx) (4 registers each, ixgbe_type.h:472-473) with an unbounded vf β€” vf >= 128 in the 8-queue MRQC mode already yields idx β‰₯ 32 β†’ far out of the WQBR window.
  • ixgbe_mdd_event_X550 (:1489-1490) writes vf_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).

VERDICT.md
↓ download raw

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:

  1. ixgbe_x550.c:1078 β€” int vf_target_reg = vf >> 3; … :1084/:1090 IXGBE_READ_REG/WRITE_REG(hw, IXGBE_PFVFSPOOF(vf_target_reg), …). IXGBE_PFVFSPOOF(_i) (ixgbe_type.h:534) is 0x08200 + 4*i with exactly 8 registers (IXGBE_PFVFSPOOF_REG_COUNT, ixgbe_type.h:557). vf >= 64 (or negative) indexes outside the block β†’ OOB MMIO read-modify-write.
  2. ixgbe_x550.c:1429-1436 β€” start_q = vf * num_qs (num_qs from the live MRQC mode, 2/4/8), idx = start_q / 32, then IXGBE_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; vf beyond the mode's pool count walks out of the window.
  3. 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: rg over 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_X550 has 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/src with -Werror (DF-3726_make_rc=0, ../DF-3724/fix_build.log), reverted after (md5 restored).

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

inconclusive
baseline no→ patch + rebuild →patched clean

fix.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
↓ fix.diffper-fix-DF-3726

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.