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

e1000_release_swfw_sync_80003es2lan acquires the SWSM hardware semaphore in an unbounded empty-body loop on every PHY/KMRN/NVM release path: a stuck SMBI bit pins a CPU forever under the driver serializer

Summary

The release path takes the hardware semaphore via an unbounded empty-body while loop. The per-call acquire is bounded (~1.6s MMIO spin max) but returns failure on timeout, so a stuck SMBI bit (ASF/management-firmware hold, wedged sibling function on the dual-port part, post-bus-error device) makes the outer loop retry forever. Every PHY/KMRN/NVM access and reset funnels through it under the igb serializer: CPU livelock + interface wedge. The acquire side is bounded; the release side lost the bound. Identical upstream-inherited shape at 82575.c and i210.c. Bound-the-release-retry fix (replicate x3).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4306 Β· 6 files
FileTypeDescriptionSize
README.md β€” 2.5 KB ↓ raw
VERDICT.md β€” 4.6 KB ↓ raw
env.txt β€” 1.2 KB view raw
fix.diff β€” 847 B view raw
manifest.json β€” 1021 B view raw
verdict.json β€” 3.6 KB view raw

DF-4306 β€” e1000_release_swfw_sync_80003es2lan spins unboundedly acquiring the HW semaphore on every PHY/NVM release path

File: sys/dev/netif/ig_hal/e1000_80003es2lan.c (pass 2, no known findings) Lines: 449-463 (e1000_release_swfw_sync_80003es2lan, empty-body while at :455-456), reached from every PHY/KMRN/NVM release on this MAC: :499/:514/:531 (gg82563 read), :570/:585/:602 (gg82563 write), :318 (phy release wrapper), :353 (KMRN mac_csr release), :375/:391 (NVM release), :851 (reset_hw). Family shape: while (e1000_get_hw_semaphore_generic(hw) != E1000_SUCCESS); β€” identical pattern at e1000_82575.c:1110 and e1000_i210.c:146 (upstream-inherited Intel HAL; Linux e1000e has the same construct).

The bug

The SW/FW-sync acquire side is bounded (50 Γ— 5 ms, :402-439), but the release side takes the SWSM hardware semaphore in an unbounded retry loop:

while (e1000_get_hw_semaphore_generic(hw) != E1000_SUCCESS)
    ; /* Empty */                       /* e1000_80003es2lan.c:455-456 */

e1000_get_hw_semaphore_generic (e1000_mac.c:1721) is itself a bounded busy-wait (timeout = nvm.word_size + 1 iterations of usec_delay(50); word_size ≀ 16384 by the :176-178 cap β‡’ up to ~1.6 s of MMIO spin per attempt) that returns -E1000_ERR_NVM when the SMBI bit stays set. A stuck SMBI bit β€” management-firmware hold (this MAC reports asf_firmware_present = TRUE, :223), a second port's driver wedged, or a device gone south after a bus error β€” therefore turns every release call into an infinite ~1.6 s-step MMIO spin. All call sites run under the igb driver serializer; the spinning thread pins its CPU, burns the PCI link with MMIO reads, and blocks every other entry point on the interface until reboot.

Severity Low per rubric: "narrow trigger … requires specific hardware" (the 80003ES2LAN NIC plus a wedged SMBI bit β€” a hardware/firmware fault, not attacker-controllable state). The code shape itself is certain.

Guest status

No 80003ES2LAN (or any igb) device on this guest (env.txt: hw.igb unknown oid, virtio-only); not_reproduced per family convention (cf. DF-3827). Defect certain from source.

Fix

Bound the retry (mirroring the acquire-side 50Γ—5 ms budget) and bail with a debug print instead of spinning forever β€” see fix.diff (patch -p1 --dry-run clean). Trade-off documented in the diff: bailing leaves the SW_FW_SYNC bit set (PHY/NVM access bricked) but saves the CPU and the system; the NIC is unusable either way with a stuck semaphore.

VERDICT.md
↓ download raw

DF-4306 VERDICT β€” unbounded HW-semaphore spin in the 80003ES2LAN SWFW release path

Bottom line

Source-certain unbounded-wait defect (DF-4217 family shape: a hardware wait whose timeout branch can never be reached because the loop condition never terminates). Runtime not reproducible on this guest β€” no 80003ES2LAN device exists (hw.igb unknown oid, virtio-only guest; QEMU's Intel NIC models are e1000-family MMIO-incompatible with the 80003 KMRN/SWFW register set and no such device is attached anyway) β€” not_reproduced per family convention (DF-3827). Impact ceiling on real hardware: CPU livelock + interface wedge (availability only). Low severity: trigger is a hardware/firmware fault state (stuck SWSM.SMBI bit), not attacker-set state.

Trace

  1. e1000_release_swfw_sync_80003es2lan (e1000_80003es2lan.c:449-463): while (e1000_get_hw_semaphore_generic(hw) != E1000_SUCCESS) ; at :455-456 β€” no bound, no yield, MMIO read loop.
  2. e1000_get_hw_semaphore_generic (e1000_mac.c:1721-1745): bounded per-call (SMBI poll timeout = hw->nvm.word_size + 1 Γ— 50 Β΅s; word_size capped to 1<<14 by e1000_80003es2lan.c:176-178), returns -E1000_ERR_NVM on timeout β‡’ the outer while retries the entire bounded poll forever.
  3. Reachability: every PHY register access (e1000_read/write_phy_reg_ gg82563_80003es2lan :482/:499/:514/:531, :553/:570/:585/:602), every KMRN access (:1388/:1402, :1425/:1436), NVM acquire/release wrapper (:368-391), and e1000_reset_hw_80003es2lan (:845-851) funnel through acquire/release β€” i.e. link maintenance (1 Hz igb tick), ifmedia changes, resets. All execute under the igb serializer.
  4. Why SMBI sticks: the 80003 pairs with manageability firmware (mac->asf_firmware_present = TRUE :223; FWSM-checked arc_subsystem_valid :227-228) and dual-function parts share the semaphore β€” a fw hold or a wedged sibling function leaves SMBI set indefinitely (this is why the acquire side has a timeout at all; the release side just forgot the bound).

Checklist coverage (whole-file negative results)

  • (a) init_function_pointers NULL-op coverage: set_lan_id called at :269 is pre-assigned by e1000_init_mac_ops_generic (e1000_mac.c:58) which e1000_setup_init_funcs (e1000_api.c:447) runs BEFORE the family init (:486) β€” safe. Serdes (non-copper) early-return leaves phy read/write NULL, but both local call sites guard (if (!(hw->phy.ops.read_reg)) :670/:765) and cfg_on_link_up is only invoked on the copper branch of e1000_get_link_up_info_80003es2lan (:800-803). Clean.
  • (b) rar/LAA: no direct RAR mutation in this file (delegates to e1000_init_rx_addrs_generic/e1000_check_alt_mac_addr_generic); rar_entry_count = E1000_RAR_ENTRIES (:221). Clean.
  • (c) PHY modes: KMRN retry do/while loops bounded by GG82563_MAX_KMRN_RETRY=5 (e1000_80003es2lan.h:92) at :1312/:1363; smart-speed not implemented here; cable-length table indexing guarded (index >= GG82563_CABLE_LENGTH_TABLE_SIZE - 5 :774-775 with mask 0x0007, table[11] β‡’ max touch table[10]). Clean.
  • (d) NVM ops: word_size derived from EECD with the size > 14 β†’ 14 cap (:176-178, 16 K words max); write delegates to e1000_write_nvm_spi. The mdic_wa verify-read at :511 ignores its ret_val before comparing temp (:513) β€” temp is pre-initialized (:496), worst case a wrong page read is returned; verbatim upstream Intel HAL, no kernel memory effect (not filed; hardening note only).
  • (e) semaphore paths: acquire bounded (:412-431); release unbounded (:455) β€” this finding; e1000_get_cfg_done bounded (PHY_CFG_TIMEOUT Γ— 1 ms, :641-646).
  • (f) div0: the only division is the constant /2 at :780. Clean.
  • (g) FreeBSD divergence: file is byte-carried from FreeBSD's ig_hal (Intel-shared source, $FreeBSD$ tag :33); no DragonFly-local modifications of substance in this snapshot (single bulk-import commit in history).

Fix validation

fix.diff bounds the retry (50 attempts, matching the acquire-side budget) with a DEBUGOUT on give-up; patch -p1 --dry-run clean. Same-shape instances exist at e1000_82575.c:1110 and e1000_i210.c:146 β€” the fix should be replicated there (noted; those files are separate audit rows). Cannot be runtime-validated without 80003 hardware β†’ 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_testable
baseline no→ patch + rebuild →patched clean

fix.diff authored against the read-only sys/ tree (patch -p1 --dry-run clean); cannot be runtime-validated without 80003ES2LAN hardware.

findings/poc/DF-4306/fix.diff
↓ fix.diffper-fix-DF-4306

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (path:line trace incl. per-call bound analysis of e1000_get_hw_semaphore_generic and the whole-file checklist negative results); env.txt (hw.igb unknown oid, virtio-only guest, guest src md5 == audited tree); fix.diff (bounded retry, patch -p1 --dry-run clean)

PoC changes

No seed; no runnable PoC exists without an 80003ES2LAN device - pack documents the source proof, per-call cost analysis, family cross-references, and the fix.

Verified recommended fix

Bound the release retry to the acquire-side budget (e.g. 50 attempts) and give up with a debug print instead of spinning forever; replicate at e1000_82575.c:1110 and e1000_i210.c:146; see fix.diff.

Verdict

Source-certain unbounded-wait defect (DF-4217 family shape): e1000_release_swfw_sync_80003es2lan (sys/dev/netif/ig_hal/e1000_80003es2lan.c:449-463) acquires the SWSM hardware semaphore in an empty-body unbounded loop (:455-456). e1000_get_hw_semaphore_generic (e1000_mac.c:1721) is bounded per call (word_size+1 iterations of usec_delay(50), word_size capped at 16384 by :176-178) and returns -E1000_ERR_NVM on timeout, so a stuck SMBI bit (ASF/management-firmware hold - this MAC sets asf_firmware_present=TRUE at :223 - wedged sibling function on the dual-port part, or post-bus-error device) makes the outer loop retry the full ~1.6s MMIO poll forever. Every PHY register access (:482/:499/:514/:531, :553/:570/:585/:602), KMRN access (:1388/:1402/:1425/:1436), NVM wrapper path (:368-391) and reset_hw (:845-851) funnel through this release while the caller holds the igb driver serializer: a stuck semaphore pins one CPU at 100% MMIO spin and wedges every interface entry point until reboot. The acquire side is bounded (50x5ms, :412-431) - the release side simply lost the bound. Identical upstream-inherited shape at e1000_82575.c:1110 and e1000_i210.c:146. Kernel-path reproduction impossible on this guest: no 80003ES2LAN/igb device exists (env.txt: hw.igb unknown oid, virtio-only) - not_reproduced per family convention (DF-3827). Impact ceiling: CPU livelock + interface wedge on real hardware; trigger is a hardware/firmware fault state, not attacker-controllable - Low severity, availability only.