Unchecked PHY-read return values: EEE decisions and PHY write-backs derived from uninitialized kernel stack
Summary
Five clusters discard the return of hw->phy.ops.read_reg* and then use the destination u16, which is uninitialized stack when the read fails: e1000_set_eee_pchlan (gates 100M-EEE LPI enable on stack garbage); e1000_check_for_copper_link_ich8lan (masks/ORs stack garbage and writes it back into I217_PLL_CLOCK_GATE_REG/HV_PM_CTRL and HV_KMRN_FIFO_CTRLSTA); e1000_lv_jumbo_workaround_ich8lan (writes stack-garbage-derived PHY config under masks). On a PHY whose MDIO reads fail (failing or malicious hardware; SMBus-mode transitions), non-deterministic kernel-stack content is written into PHY analog/clock-gate/FIFO config registers and drives EEE decisions. Values never copied to userspace - no direct info leak; KMSAN-class defect with link-instability ceiling. Runs from the link watchdog/interrupt path on every link event on PCH hardware. Identical in FreeBSD HEAD - vendor-inherited.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3643 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.8 KB | β raw | |
| VERDICT.md | β | 2.5 KB | β raw | |
| trace.sh | β | 1.1 KB | view raw | |
| trace.log | β | 3.2 KB | view raw | |
| guest_env.txt | β | 1.5 KB | view raw | |
| fix.diff | β | 1.7 KB | view raw | |
| verdict.json | β | 2.9 KB | view raw | |
| manifest.json | β | 931 B | view raw |
DF-3643 β unchecked PHY-read return values: decisions and PHY write-backs on uninitialized stack variables
What / where
sys/dev/netif/ig_hal/e1000_ich8lan.c β several PHY register reads ignore the
hw->phy.ops.read_reg* return value and then use (and write back) the destination
variable, which is an uninitialized stack u16 when the read fails:
| site | lines | variable | use after failed read |
|---|---|---|---|
e1000_set_eee_pchlan |
959-968 | data (decl. 909, uninit) |
branch data & NWAY_LPAR_100TX_FD_CAPS selects whether 100M EEE LPI is enabled |
e1000_check_for_copper_link_ich8lan |
1617-1626 | phy_reg (decl. 1552, uninit) |
masked/OR'd and written back to I217_PLL_CLOCK_GATE_REG / HV_PM_CTRL |
| same | 1765-1772 | phy_reg |
masked and written back to HV_KMRN_FIFO_CTRLSTA |
e1000_lv_jumbo_workaround_ich8lan |
2771-2773 | phy_reg |
OR'd with (1<<14) and written back; final :2923 write-back re-enables Rx from the same garbage |
| same | 2837-2859, 2896-2917 | data |
several read-mask-write RMWs into PHY config |
On read failure the PHY was not written, so the variable keeps whatever was on the kernel stack; the code then writes that garbage (partially masked) into PHY analog/config registers or bases an EEE enable decision on it.
Reachability
e1000_check_for_copper_link_ich8lanruns from the link-status interrupt / watchdog path on every link event on PCH hardware (mac.ops.check_for_link).e1000_set_eee_pchlanruns after every link-up for 82579/I217 PHYs (e1000_ich8lan.c:1784-1788).e1000_lv_jumbo_workaround_ich8lanruns on MTU changes to >1500 (if_em ioctl path).
A read failure is the precondition (PHY in reset/MDIO error/SMBus-mode transition) β on healthy hardware rare; on failing or malicious hardware inducible.
Impact
Uninitialized-kernel-stack-influenced PHY configuration (register write-backs of stack garbage under masks). The value is never copied to userspace, so there is no direct info leak; the outcome is a mis-programmed PHY (link instability at worst) and a KMSAN-class uninitialized-read defect. Severity: Info (hardening).
Reproduce (source-level)
sh findings/poc/DF-3643/trace.sh # regenerates trace.log with all cited lines git apply --check findings/poc/DF-3643/fix.diff
Runtime reproduction requires Intel PCH GbE PHY hardware whose MDIO reads can be made to fail; the audit guest has only virtio NICs (see guest_env.txt), so the defect is proven at source level; identical code exists in FreeBSD HEAD (upstream-inherited).
Expected result after fix
Failed PHY reads abort the EEE decision (goto release), return an error from the
link-check path, or conservatively use 0-initialized values for RMW chains, so no
decision or write-back is derived from uninitialized stack.
VERDICT β DF-3643
Status: not_reproduced (runtime) β defect certain (source), upstream-inherited.
What I did
- Enumerated every
hw->phy.ops.read_reg*call site in the file whose return value is discarded and whose destination is consumed afterwards. The five clusters above (trace.log) are the complete set; all other read sites either check the return or do not use the destination on the error path. - Confirmed each destination is genuinely uninitialized at first use on the
failing path (
datadecl. 909,phy_regdecl. 1552 / 2761) β these are per-call stack frames, so on repeated watchdog invocations the slot contains stale data from the previous call or arbitrary caller frames, i.e. non-deterministic kernel-stack content flows into PHY register writes. - Verified the code is byte-identical in FreeBSD HEAD (upstream-inherited) and that DragonFly's copy is a faithful vendor snapshot (diff vs FreeBSD stable/12 contains no local edits to these functions).
- Guest verification (
guest_env.txt): no Intel GbE MAC on the QEMU guest (virtio only), so the PHY paths never execute there; runtime reproduction is impossible on this rig. Deterministically forcingread_regfailures requires real (or emulated-FPGA) PHY hardware.
Impact ceiling (honest)
- No kernel memory corruption, no userspace info leak (values are only written into PHY config registers and used in branches).
- Worst realistic outcome: garbage analog/clock-gate/FIFO config written to the PHY while it is in an error state β link flaps/instability, i.e. a robustness defect more than a security boundary crossing. KMSAN would flag every site.
- Classified Info (hardening) per the audit rubric.
Fix
fix.diff (git-apply-able, git apply --check verified): check the return of the
PHY_LP_ABILITY read (EEE decision aborts to release), check the
HV_KMRN_FIFO_CTRLSTA read (error returned to the link-check caller), check the
PHY_REG(769,20) jumbo Rx-disable read, and zero-initialize the RMW chain locals
so any remaining unchecked read degrades to defined defaults. Fix validation on
hardware was not possible on this guest (fix_status: not_testable).
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_testableNot testable on this guest: forcing PHY read failures requires real Intel GbE PHY hardware; guest has virtio only. fix.diff verified to apply cleanly (git apply --check) and reviewed per-site.
findings/poc/DF-3643/fix.diff; VERDICT.md; guest_env.txt (hardware absent)
Confirmed kernel references
Detail
Evidence (decisive lines)
trace.log (all cited sites with declarations); guest_env.txt (no e1000 hardware); fix.diff (git apply --check clean); VERDICT.md (complete site enumeration + reasoning)
PoC changes
n/a - no seeded PoC; evidence pack built from scratch as a source-level trace + upstream-identity proof
Verified recommended fix
Check read_reg return values at the PHY_LP_ABILITY, HV_KMRN_FIFO_CTRLSTA and PHY_REG(769,20) sites and abort on failure; zero-initialize the remaining RMW chain locals.
Verdict
Source-certain uninitialized-stack-use defect, runtime-untestable on this guest. Five call-site clusters discard the return of hw->phy.ops.read_reg* and then branch on / mask / write back the destination u16, which is uninitialized kernel stack when the read fails: e1000_set_eee_pchlan:959 (data, decl 909) drives the 100M-EEE LPI enable decision; e1000_check_for_copper_link_ich8lan:1617-1626 and 1765-1772 (phy_reg, decl 1552) write stack-garbage-derived values back into I217_PLL_CLOCK_GATE_REG/HV_PM_CTRL and HV_KMRN_FIFO_CTRLSTA; e1000_lv_jumbo_workaround_ich8lan:2771-2773 plus RMW chain 2837-2917 write garbage PHY config under masks. Identical in FreeBSD HEAD (vendor-inherited). Guest has no Intel GbE PHY (virtio only) so the paths never execute there. No userspace copy-out => no direct info leak; ceiling is mis-programmed PHY config (link instability) and KMSAN-class uninit reads. Severity Info (hardening).
No comments yet.