# 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_another` walk — loops as long as the card keeps FIFO_PORTS non-empty (`FIFO_REMPTY` never 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_TIME` 1000) — 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)

```diff
--- 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`.
