# DF-3807 — mskc_handle_events() infinite drain on a status LE naming an unattached port

## Verdict

**Mechanism PROVEN on the guest kernel by a deterministic replica of
if_msk.c's exact mskc_handle_events()/mskc_intr() drain loop over a
synthetic 2048-entry status ring; msk-specific runtime hardware-gated
(the port bit is written by the NIC's status unit — needs faulty/hostile
Yukon hardware or errant DMA).** Severity Medium, confidence certain.

## Root cause (path:line)

`mskc_handle_events()` (sys/dev/netif/msk/if_msk.c:3137-3238) walks the
DMA'd status ring from `sc->msk_stat_cons` to the device-supplied
`STAT_PUT_IDX`:

```c
3153:	for (cons = sc->msk_stat_cons; cons != idx;) {
3155:		control = le32toh(sd->msk_control);
3156:		if ((control & HW_OWNER) == 0)
3157:			break;
3165:		control &= ~HW_OWNER;
3166:		sd->msk_control = htole32(control);   /* LE released */
3169:		port = (control >> 16) & 0x01;
3170:		sc_if = sc->msk_if[port];
3171:		if (sc_if == NULL) {
3172:			device_printf(sc->msk_dev, "invalid port opcode "
3173:			    "0x%08x\n", control & STLE_OP_MASK);
3174:			continue;                         /* <-- BUG */
3175:		}
...
3224:		MSK_INC(cons, MSK_STAT_RING_CNT);      /* ONLY advance */
```

The `continue` at :3174 skips the loop's **only** consumer-index advance
(`MSK_INC` at :3224). For a status LE whose port bit names a port with no
interface attached — `msk_if[1] == NULL` on every single-port adapter
(only attached ports are registered, if_msk.c:1383, children created at
:1699-1719; a failed port attach also NULLs the slot, :1469) — the first
pass releases the LE (HW_OWNER cleared) and `continue`s with `cons`
unchanged; the second pass re-reads the same LE, sees HW_OWNER clear and
`break`s. `msk_stat_cons` never advances while `STAT_PUT_IDX` is past it,
so `mskc_handle_events()` (:3237 `return (cons != STAT_PUT_IDX)`) returns
non-zero **forever**, and `mskc_intr()`'s outer drain

```c
3291:	while (mskc_handle_events(sc) != 0)
3292:		;
```

spins permanently — **with `sc->msk_serializer` held** (the IRQ is muxed
through it, if_msk.c:1729-1731) and **before the interrupt is re-enabled**
(`B0_Y2_SP_ICR` write at :3297): hard system hang, both ports of the
controller wedged, the interrupt never completes. The driver itself
believes this input is possible — it prints "invalid port opcode" — but
the handling hangs the box instead of skipping the LE.

Note the process-limit bound (`rxprog > sc->msk_process_limit`, :3225)
also sits after the skipped increment, so it never guards this path
either; and even without the NULL-port bug, the outer `while` re-enters
`mskc_handle_events` after a process-limit break, making the limit a
per-chunk rather than a per-interrupt bound (drain-to-empty design shared
with upstream FreeBSD — noted, not filed).

## Guest proof (deterministic replica, exact loop shape)

`msk_hevent_replica.ko` (fake `msk_stat_desc` ring of
`MSK_STAT_RING_CNT=2048` entries, `STAT_PUT_IDX=5`, LE0 =
`HW_OWNER | port 1` where `msk_if[1] == NULL`, LE1..4 ordinary):

- **fix-shape control** (`MSK_INC` in the NULL-port branch = fix.diff):
  `mskrep2[fixed]: drain returned 0 after 0 outer iterations, stat_cons=5
  idx=5 rx_frames=4 — interrupt re-enabled (B0_Y2_SP_ICR), ISR RETURNS`
- **shipped shape**: `SPINNING outer=67108864 … outer=88046829568
  stat_cons=0 idx=5 (no progress)` — **88 billion** drain iterations,
  consumer index stuck at 0, ~670M iterations/s, zero progress; the guest
  became unusable to ssh from the kernel-priority spin (no panic — pure
  permanent spin/starvation). Killed only via the module's documented
  instrumentation escape; the real driver has none.

Files: `run.log` (session transcript incl. the fixed-shape control),
`serial_console.txt` (decisive console sequence + spin-marker count +
no-panic check), `build.log`, `msk_hevent_replica.c`, `fix.diff`,
`env.txt`.

## Fix

`fix.diff`: advance the consumer past the offending LE before
`continue` (`MSK_INC(cons, MSK_STAT_RING_CNT)` in the NULL-port branch).
Mechanism validated live (fixed-shape control returns, consumer advances,
ISR completes). Driver-level validation needs Yukon hardware →
fix_status=not_testable.

## Kernel references

sys/dev/netif/msk/if_msk.c:1699-1719 (children only for attached ports),
:1383, :1469, :1729-1731 (IRQ muxed through serializer), :3137-3238
(:3153, :3156-3157, :3165-3166, :3169-3174, :3224, :3225, :3229, :3237),
:3291-3297.

## 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.
