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

Unchecked PHY/EEPROM read return values across ixgbe_common.c: config decisions from uninitialized kernel stack, and a failed PHY read written back clobbering the AN advertisement (DF-3643 parity)

Summary

Five clusters discard hw->phy.ops.read_reg / hw->eeprom.ops.read return values and act on the results: ixgbe_set_lan_id_multi_port_pcie (bus->instance_id from uninitialized stack u16 on read failure); ixgbe_setup_fc_generic (failed copper PHY read leaves reg_cu=0 which is then WRITTEN BACK to IXGBE_MDIO_AUTO_NEG_ADVT, silently zeroing all non-FC advertisement bits - link renegotiation breakage); ixgbe_fc_autoneg_copper (failed reads silently degrade flow-control negotiation); ixgbe_get_device_caps_generic (start_hw branches on uninitialized stack device_caps to decide need_crosstalk_fix); clear_hw_cntrs (benign). Upstream-identical vendored code (diff-verified) - decisions only, no userspace memory disclosure. Hardening in vendored Intel shared code (fix owner: upstream), matching DF-3643.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3671 Β· 8 files
FileTypeDescriptionSize
README.md β€” 2.4 KB ↓ raw
VERDICT.md β€” 2.7 KB ↓ raw
fix.diff β€” 2.4 KB view raw
build_baseline.log β€” 15.8 KB view raw
build_patched.log β€” 15.8 KB view raw
env.txt β€” 1.0 KB view raw
manifest.json β€” 667 B view raw
verdict.json β€” 2.5 KB view raw

DF-3671 β€” ixgbe_common.c: unchecked PHY/EEPMD read returns feed config decisions from uninitialized/zeroed state

File: sys/dev/netif/ix/ixgbe_common.c Β· Severity: Info (hardening; DF-3643 parity) Β· Class: error-handling discipline

Sites (all upstream-identical vendored shared code; no divergence found)

  1. ixgbe_clear_hw_cntrs_generic ixgbe_common.c:621-628 β€” four hw->phy.ops.read_reg(..., &i) calls with discarded return values (X540/ X550 path). On failure nothing is decided from them (results unused), so impact is nil here; included for completeness.
  2. ixgbe_set_lan_id_multi_port_pcie :1095 β€” hw->eeprom.ops.read(hw, IXGBE_EEPROM_CTRL_4, &ee_ctrl_4) unchecked; on failure ee_ctrl_4 is an uninitialized stack u16 used to derive bus->instance_id (CS4227 MAC instance selection) β€” config from garbage.
  3. ixgbe_setup_fc_generic :278-279 β€” copper path reads the PHY AN advertisement with the return discarded; reg_cu stays 0 and is then written back to IXGBE_MDIO_AUTO_NEG_ADVT at :375-376 after OR-ing in pause bits β€” a failed read turns into a PHY register clobber that silently drops all non-FC advertisement bits (link renegotiation breakage).
  4. ixgbe_fc_autoneg_copper :3083-3088 β€” two unchecked PHY reads whose (zero-initialized) results feed ixgbe_negotiate_fc; zero inputs happen to be rejected there (!(adv_reg) check at 2953), so failure degrades to fc_none instead of an explicit error β€” silent misconfiguration.
  5. ixgbe_get_device_caps_generic :4438 β€” unchecked EEPROM read; the caller ixgbe_start_hw_generic (:397/:430-431) passes the address of an uninitialized stack u16 and then tests device_caps & IXGBE_DEVICE_CAPS_NO_CROSSTALK_WR β€” the crosstalk-fix decision (hw->need_crosstalk_fix) is made from uninitialized kernel stack memory.

No path returns these values to userspace, so this is a correctness/hardening defect, not a leak β€” same classification as DF-3643 (Info).

PoC / verification

Hardware-gated (PHY/EEPROM read failures need a malfunctioning or malicious NIC/PHY). No runnable PoC; guest has no ixgbe device (env.txt). fix.diff checks all five return values (device_caps zero-initialized + error propagation, PHY reads bailing with IXGBE_ERR_PHY, skipped write-back on failed read) and compile-validates on the guest's md5-identical source (build_patched.log, -Werror clean).

VERDICT.md
↓ download raw

DF-3671 β€” VERDICT

Status: inconclusive (runtime) / source-level certain. Severity Info: no kernel-memory disclosure, decisions-only uninitialized/garbage inputs. DF-3643 parity for the ixgbe HAL.

Analysis

The e1000_ich8lan pass-1 lesson (DF-3643: "Unchecked PHY-read return values: EEE decisions and PHY write-backs derived from uninitialized kernel stack") applies verbatim to five clusters in the ixgbe shared code; none of them propagates the read status:

Site Discarded return Downstream use
ixgbe_common.c:621-628 4Γ— phy.ops.read_reg(...,&i) none (counter clear) β€” benign
ixgbe_common.c:1095 eeprom.ops.read(EE_CTRL_4, &ee_ctrl_4) bus->instance_id from uninit stack u16
ixgbe_common.c:278-279 phy.ops.read_reg(AN_ADVT, &reg_cu) **write-back at :375-376 clobbers PHY advertisement with 0
ixgbe_common.c:3083-3088 2Γ— phy.ops.read_reg ixgbe_negotiate_fc inputs; 0 inputs silently degrade to ixgbe_fc_none
ixgbe_common.c:4438 eeprom.ops.read(DEVICE_CAPS, device_caps) ixgbe_start_hw_generic (:397, :430-431) branches on uninitialized stack for need_crosstalk_fix

Reachability: all execute during ix_start_hw/ix_setup_fc/ix_init β€” i.e., attach and every interface init on real ixgbe hardware. The reads fail when the PHY/MDIO bus is wedged, during overtemp resets, or under a hostile PHY β€” precisely the states where making config decisions from garbage compounds the fault.

Impact ceiling: wrong flow-control negotiation, broken link advertisement (site 3 actively corrupts PHY state), wrong crosstalk-fix enablement. No userspace-visible memory disclosure β†’ Info, matching DF-3643.

Upstream fidelity

Function-level diffs against FreeBSD head shared code (ixgbe_fc_autoneg_copper, ixgbe_setup_fc_generic) came back IDENTICAL for these sites β€” FreeBSD carries the same pattern; this is vendored-code divergence logging (fix owner: Intel shared code), filed for completeness and for the local hardening patch.

Verification performed

Source walk only + fix compile validation (fix.diff adds return-value checks; build_patched.log -Werror clean on the guest's md5-identical /usr/src). Runtime reproduction requires a failing PHY on real hardware β€” absent on this guest (env.txt). Guest source restored; guest left clean.

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

not_testable
baseline no→ patch + rebuild →patched clean

Compile-validated only; runtime validation needs hardware.

build_baseline.log / build_patched.log (-Werror clean with fix).
↓ fix.diffper-fix-DF-3671

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (site table with path:line); build_patched.log; env.txt

PoC changes

New pass-2 finding, no seed.

Verified recommended fix

Check every phy.ops/eeprom.ops read return; zero-init *device_caps; skip the PHY write-back when the preceding read failed (fix.diff).

Verdict

Source-level certain, Info-class (DF-3643 parity): five clusters in ixgbe_common.c discard PHY/EEPROM read return values and act on the (uninitialized or zeroed) outputs β€” ixgbe_common.c:621-628 (benign), :1095 (instance_id from uninit stack u16), :278-279+375-376 (failed PHY read written back, clobbering the PHY AN advertisement), :3083-3088 (FC negotiation silently degrades to fc_none on failed reads), :4438 + consumer ixgbe_start_hw_generic :397/:430-431 (need_crosstalk_fix decided from uninitialized stack). No kernel-memory disclosure to userspace; decisions only. Upstream FreeBSD shared code is IDENTICAL at these sites (diff verified) β€” vendored-code issue, filed for hardening. Hardware-gated; guest has no ixgbe device (env.txt). fix.diff checks all five returns; compile-validated -Werror-clean on md5-identical guest source.