Four unbounded MMUCR_BUSY busy-waits (two in hard interrupt context) plus no-exit device-driven loops: unrecoverable system hang on SMC91xx glitch or PCMCIA ejection
Summary
The driver spins on the on-chip MMU engine with ZERO timeout at four sites - two of them in hard interrupt context, one executed for every received frame. On a glitching card or after physical ejection (floating port reads keep BUSY asserted), the spins never terminate while holding the ifnet serializer in interrupt context - total system hang, power-cycle required; pccard detach cannot recover (needs the same serializer). The driver demonstrably knows the bounded idiom (its allocation wait is capped). Bound-all-waits fix using the driver's own MEMORY_WAIT_TIME.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4355 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.3 KB | β raw | |
| VERDICT.md | β | 4.7 KB | β raw | |
| fix.diff | β | 2.1 KB | view raw | |
| env.txt | β | 818 B | view raw | |
| verdict.json | β | 3.3 KB | view raw | |
| manifest.json | β | 1.2 KB | view raw |
DF-4355 β Unbounded MMU busy-waits and device-fault infinite loops: unrecoverable system hang on SMC91xx fault/removal
File: sys/dev/netif/sn/if_sn.c
Verdict status: untested β hardware-gated (SMC91xx ISA/PCMCIA NIC absent from the QEMU guest). Source-level certainty: certain.
Class: CWE-835 (loop with unreachable exit condition, kernel context) β local DoS (hard hang requiring power cycle).
Where (all sites)
Unbounded while (inw(BASE + MMU_CMD_REG_W) & MMUCR_BUSY); spins β no timeout, no bail-out:
| Site | Context | Locks held while spinning |
|---|---|---|
| if_sn.c:275-276 | sninit() MMU reset wait |
ifnet serializer (ioctl/watchdog) |
| if_sn.c:641-642 | snresume() wrong-size alloc release |
ifnet serializer (hard IRQ) |
| if_sn.c:871-872 | sn_intr() TX-error MMUCR_FREEPKT |
ifnet serializer (hard IRQ) |
| if_sn.c:1056-1057 | snread() packet release, per packet |
ifnet serializer (hard IRQ) |
Related no-exit device-driven loops in the same paths:
- if_sn.c:354+446-450:
snstart()ARR_FAILED βifq_prependβgoto startagainβ infinite re-dequeue/re-alloc retry at serializer context with a card whose allocation always fails. - if_sn.c:1063-1067:
snread()read_anotherwalk β loops as long as the card keeps FIFO_PORTS non-empty (FIFO_REMPTYnever set); a wedged card (or one whose I/O floats after PCMCIA ejection) pins the RX interrupt forever. - if_sn.c:415-419: bounded (
MEMORY_WAIT_TIME1000) β OK by contrast; shows the author knew how to bound waits but did not apply it to the above.
Trigger
- Physical ejection of the PCMCIA card while the interface is up (mid-TX or mid-RX): on pccard, port reads after removal float (typically 0xFFFF) β
MMUCR_BUSY(0x0001) reads as set forever β the spin at 871/1056 (hard-interrupt context, serializer held) never exits. Machine is dead; only a hard reset recovers. - A glitching/failing SMC91xx that never clears MMUCR_BUSY or never empties the RX FIFO produces the same hang from a transmit error interrupt.
sn_pccard_detach()cannot save you: it must acquire the same ifnet serializer the spinning interrupt holds (cf. DF-2186 in if_sn_pccard.c) β the detach path also deadlocks.
Impact
Permanent kernel hang (all-contexts DoS) on machines with the hardware. Requires physical presence (card yank) or faulty hardware β hence Low severity, but certain at source level, and a well-known bug class the project treats as fileable for hardware-gated drivers.
Fix (sketch β bounded wait helper)
--- a/sys/dev/netif/sn/if_sn.c
+++ b/sys/dev/netif/sn/if_sn.c
@@ -272,8 +272,13 @@ sninit(void *xsc)
SMC_SELECT_BANK(2);
outw(BASE + MMU_CMD_REG_W, MMUCR_RESET);
- while (inw(BASE + MMU_CMD_REG_W) & MMUCR_BUSY) /* NOTHING */
- ;
+ {
+ int t = MEMORY_WAIT_TIME * 1000;
+ while ((inw(BASE + MMU_CMD_REG_W) & MMUCR_BUSY) && --t)
+ DELAY(1);
+ if (t == 0) {
+ device_printf(sc->dev, "MMU busy timeout\n");
+ return; /* leave interface down; watchdog retries */
+ }
+ }
(the same bounded-wait pattern must replace the spins at if_sn.c:641-642, 871-872 and 1056-1057, and the read_another/startagain loops need iteration caps).
Reproduce
Not runnable on the audit guest (no PCMCIA/SMC91xx hardware; driver is optional sn
module, pccard front-end only β see env.txt). Source-level proof in VERDICT.md.
DF-4355 β VERDICT
Status: untested (hardware-gated) Β· Source-level confidence: certain Β· Expected on real hardware: unrecoverable system hang (interrupt-context / serializer-held infinite spin).
Root cause (path:line)
The SMC91xx driver waits for the on-chip MMU engine and the RX FIFO with completely unbounded busy-loops:
sys/dev/netif/sn/if_sn.c:275-276βsninit():while (inw(BASE + MMU_CMD_REG_W) & MMUCR_BUSY);afterMMUCR_RESET. Runs under the ifnet serializer (SIOCSIFFLAGS β sninit, if_sn.c:1090; watchdog β sn_intr β EPH path β sninit, if_sn.c:924-927).sys/dev/netif/sn/if_sn.c:641-642βsnresume()wrong-size-allocation release path.sys/dev/netif/sn/if_sn.c:871-872βsn_intr()TX-error path beforeMMUCR_FREEPKT. Hard interrupt context.sys/dev/netif/sn/if_sn.c:1056-1057βsnread()per-packet release beforeMMUCR_RELEASE. Hard interrupt context, inside theread_anotherloop, so it executes for every received frame.
MMUCR_BUSY is 0x0001 (sys/dev/netif/sn/if_snreg.h:238). On a card that is ejected mid-operation, pccard bus reads float (conventionally 0xFFFF), so inw() & 0x0001 is persistently true: all four sites spin forever. Sites 3-4 spin in a hard interrupt handler holding the ifnet serializer β nothing else on the system can make progress; the serial-console softclock path that would normally print a hang watchdog is starved of the interrupt β total hang, power-cycle required. The sn_pccard_detach() path cannot quiesce the driver because it needs the same serializer (see known DF-2186, sys/dev/netif/sn/if_sn_pccard.c:110-121) β detach itself deadlocks against the spinner.
Additional device-driven no-exit loops in the same fault model:
sys/dev/netif/sn/if_sn.c:446-450βARR_FAILEDβifq_prepend()βgoto startagain(354): with a card whose MMU allocation fails immediately and forever,snstart()re-dequeues the same mbuf and re-issues the allocation in a tight loop at serializer context.sys/dev/netif/sn/if_sn.c:1063-1067βread_another: no cap on the number of packets drained per interrupt; a card that keepsFIFO_REMPTY(0x8000) clear pins the RX interrupt handler indefinitely.
Contrast: the allocation wait at if_sn.c:415-419 is bounded (MEMORY_WAIT_TIME = 1000, if_snreg.h:60) β the unbounded sites are an oversight, not a style choice.
Threat model
Local attacker with physical access to a machine using an SMC91xx PCMCIA/ISA NIC (or a failing card): eject the card while the interface is up and transmitting/receiving β hang. There is no unprivileged-software-only trigger: the loops only spin when the hardware misbehaves or vanishes. Severity Low (hardware-gated DoS), confidence certain at source level.
Why untested on this guest
Same hardware gate as DF-4354: the QEMU guest has no PCMCIA slot/SMC91xx chip; if_sn is an optional sn module with a pccard-only front-end (sys/conf/files:1247, sys/dev/netif/sn/if_sn_pccard.c:126-133). Guest inventory captured in env.txt (only vtnet0/lo0). No guest-side reproduction is possible without emulating the card; classification is untested-with-source-certainty rather than not-reproduced (nothing was executed and failed β the path simply cannot exist on this machine).
Fix validation
fix_status: not_testable (same hardware gate). The fix.diff pattern (bounded MEMORY_WAIT_TIME*1000 spin with DELAY(1) and bail-out leaving the interface for the watchdog) is the same approach the driver already uses at if_sn.c:415-419; applying it at the four MMU sites plus iteration caps on the FIFO/ARR loops removes every unbounded spin. By inspection the patched loops always terminate within ~1s per wait on both healthy and vanished hardware.
Kernel references
- sys/dev/netif/sn/if_sn.c:275-276 (sninit MMU reset wait β unbounded)
- sys/dev/netif/sn/if_sn.c:641-642 (snresume wait β unbounded)
- sys/dev/netif/sn/if_sn.c:871-872 (sn_intr TX-error wait β unbounded, hard IRQ)
- sys/dev/netif/sn/if_sn.c:1056-1057 (snread release wait β unbounded, hard IRQ, per packet)
- sys/dev/netif/sn/if_sn.c:446-450 (ARR_FAILED startagain retry loop)
- sys/dev/netif/sn/if_sn.c:1063-1067 (read_another FIFO walk, no cap)
- sys/dev/netif/sn/if_sn.c:415-419 (the bounded counter-example)
- sys/dev/netif/sn/if_snreg.h:238 (MMUCR_BUSY 0x0001), :60 (MEMORY_WAIT_TIME 1000), :263 (FIFO_REMPTY 0x8000)
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: fix validation requires SMC91xx hardware (guest has none). fix.diff applies bounded waits at all four MMU sites using the driver's own MEMORY_WAIT_TIME idiom; by inspection every patched loop terminates within ~1s per wait on both healthy and vanished hardware, converting the hang into a logged timeout.
findings/poc/DF-4355/fix.diff (4 hunks); env.txt (hardware absent).
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (site-by-site table with lock context), README.md (trigger model + bounded-wait fix sketch), env.txt (guest lacks hardware), fix.diff (bounded waits at all four MMU sites). Decisive lines: if_sn.c:275, 641, 871, 1056 (unbounded spins), if_sn.c:415-419 (bounded counter-example), if_snreg.h:238/60.
PoC changes
No executable PoC exists: path requires SMC91xx PCMCIA hardware (card ejection mid-transfer) absent from the guest. Evidence pack is source-level (VERDICT.md) plus guest hardware-inventory proof (env.txt).
Verified recommended fix
Bound all four MMUCR_BUSY waits with a MEMORY_WAIT_TIME-scaled counter + DELAY(1) and bail out leaving the interface for the watchdog, mirroring the existing bounded wait at if_sn.c:415-419; cap the read_another/ARR_FAILED loops.
Verdict
The SMC91xx driver waits on the on-chip MMU engine with four completely unbounded busy-loops - if_sn.c:275-276 (sninit), 641-642 (snresume), 871-872 (sn_intr TX-error, hard IRQ context), 1056-1057 (snread, hard IRQ, per packet) - plus two device-driven no-exit loops (ARR_FAILED retry at 446-450->354; read_another FIFO walk at 1063-1067). On a glitching card or after physical PCMCIA ejection (floating port reads keep MMUCR_BUSY=0x0001 asserted), the spins never terminate while holding the ifnet serializer in interrupt context: the machine hangs and only a power cycle recovers. Source-level certainty is high (the same driver bounds its allocation wait at 415-419 with MEMORY_WAIT_TIME=1000, showing the omission is an oversight), but reproduction requires SMC91xx hardware which the QEMU guest lacks -> untested. Local DoS only, physical-presence/failing-hardware trigger, severity Low.
No comments yet.