Unbounded PHY-reset poll in xe_mii_init(): card insertion / ifconfig up hangs the kernel in crit + serializer
Summary
xe_mii_init polls PHY_BMCR_RESET with a bare while loop - no iteration budget, no timeout, no bailout. It runs inside crit_enter() and the ifnet serializer at PCMCIA card insertion and every ifconfig up. A PHY that acknowledges MII but never clears BMCR_RESET (faulty silicon or a deliberately malicious attacker-inserted PCMCIA card) pins the CPU forever; no watchdog can fire. xe_phy_readreg returns 0 on MII ack failure so an absent PHY exits - the hang needs a present-but-stuck PHY, exactly what hostile hardware produces. FreeBSD 10.3's copy carries the identical loop. Bounded-poll fix with treat-as-absent fallback.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4160 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 791 B | β raw | |
| VERDICT.md | β | 2.3 KB | β raw | |
| fix.diff | β | 531 B | view raw | |
| manifest.json | β | 611 B | view raw | |
| verdict.json | β | 1.8 KB | view raw |
DF-4160 β Unbounded PHY-reset poll in xe_mii_init()
File: sys/dev/netif/xe/if_xe.c:1637-1639 Β· Low Β· CWE-835 Β· hardware-gated
while(xe_phy_readreg(scp, PHY_BMCR) & PHY_BMCR_RESET); has no timeout and
runs in crit_enter() + ifnet serializer at PCMCIA card insertion
(xe_attach β xe_init, if_xe.c:397) and on every interface up. A PHY that
acks MII but never clears RESET (faulty or malicious card β PCMCIA is
attacker-insertable hardware) hangs the kernel permanently.
- Full analysis:
VERDICT.md - Fix:
fix.diff(bounded poll, stuck PHY treated as absent β existing 10Mbit fallback path) - No runtime verification possible: the QEMU audit guest has no PCMCIA/Xircom
hardware. Status
untested, per-audit convention for hardware-gated DoS findings.
DF-4160 β Unbounded PHY-reset poll in xe_mii_init(): attach/init hard hang
File: sys/dev/netif/xe/if_xe.c:1637-1639 Β· Severity: Low Β· CWE-835
Root cause
/* Reset the PHY */
xe_phy_writereg(scp, PHY_BMCR, PHY_BMCR_RESET);
DELAY(500);
while(xe_phy_readreg(scp, PHY_BMCR) & PHY_BMCR_RESET); /* if_xe.c:1639 */
The poll has no iteration budget, no timeout, and no bailout. It executes:
- during
xe_attach()βxe_init()(if_xe.c:397) β i.e. at PCMCIA card insertion / firstifconfig xe0 upβ insidecrit_enter()and the ifnet serializer, and - on every later interface up-transition.
A PHY that acknowledges MII transactions but never clears PHY_BMCR_RESET
(e.g. broken silicon, or a deliberately malicious PCMCIA card β the card is
external, attacker-insertable hardware) pins the kernel in this loop forever:
crit section + serializer held, CPU spinning on bit-banged MDIO. The system
hangs at card insertion with no watchdog able to fire.
Note the escape hatches that do NOT save you: if the MII transaction fails to
ack, xe_phy_readreg() returns 0 (xe_mii_readreg zeroed mii_data, ack
failure returns garbage-free 0) and the loop exits β the hang requires a PHY
that acks and keeps reporting RESET. That is exactly the "present but stuck"
case a hostile or faulty card produces.
Fix
fix.diff bounds the poll (1000 Γ 1 ms) and treats a stuck PHY as absent
(xe_mii_init then returns 0 β scp->phy_ok = 0), which the driver already
handles gracefully (10Mbit-only fallback in xe_setmedia_serialized).
Verification
Hardware-gated (needs a Xircom-class PCMCIA adapter with a misbehaving PHY); no such hardware exists in the QEMU guest, so no runtime verification is possible. Source-level certainty: the loop body and exit condition are fully visible; no hidden bound exists. FreeBSD 10.3's copy of this driver carries the identical unbounded loop (shared 1999-vintage bug).
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_testablefix bounds the poll to 1000x1ms and returns phy_ok=0 on timeout, a state the driver already handles (10Mbit fallback); kernel-level validation impossible without the hardware
fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (source trace), fix.diff (bounded poll + graceful fallback)
PoC changes
n/a (new finding, pass 2); no harness - the unbounded loop is directly visible in source
Verified recommended fix
Bound the PHY reset poll in xe_mii_init() and treat a stuck-reset PHY as absent.
Verdict
xe_mii_init() polls PHY_BMCR_RESET with an unbounded while loop (if_xe.c:1639, no timeout, no bailout) inside crit_enter() + ifnet serializer, reached at card insertion (xe_attach -> xe_init, if_xe.c:397) and on every interface up. A PHY that acks MII but never clears RESET (faulty or malicious PCMCIA card) hangs the kernel permanently. Hardware-gated: no Xircom PCMCIA hardware in the QEMU guest, so no runtime verification; source-level certainty (exit condition fully visible, no hidden bound). FreeBSD 10.3 carries the identical bug.
No comments yet.