# DF-4432 — VERDICT

**Finding:** `sys/dev/netif/sln/if_sln.c:949` — `sln_rx()` passes a NULL mbuf to
`ifp->if_input()` when `m_devget()` fails → guaranteed kernel panic.

**Verdict: REPRODUCED (impact: panic — kernel DoS).**

## The bug, line by line

1. `if_sln.c:810 sln_rx()` runs from the RX interrupt (`if_sln.c:1090-1091`) or
   the watchdog (`if_sln.c:1168`), both only with the interface UP+RUNNING
   (`if_sln.c:1075`, `if_sln.c:721`).
2. Each received frame is copied out of the 64 KB DMA ring with
   `m = m_devget(rx_bufpos - 2, pkt_size + 2, 0, ifp)` — wrap branch at
   `if_sln.c:913`, normal branch at `if_sln.c:926`.
3. `m_devget()` (sys/kern/uipc_mbuf.c:2235-2269) allocates with
   `m_getl(len, M_NOWAIT, ...)`; on allocation failure it frees the partial
   chain and **returns NULL** (uipc_mbuf.c:2247-2249).
4. `if_sln.c:915-919` / `if_sln.c:928-937`: `if (m == NULL) { ierrors++;
   if_printf(...); }` — and then **falls through** (no `continue`, no guard).
5. `if_sln.c:949`: `ifp->if_input(ifp, m, NULL, -1);` — **unconditional**,
   `m == NULL` here.
6. `ifp->if_input == ether_input` for this interface
   (`if_ethersubr.c:642` inside `ether_ifattach`, called from
   `if_sln.c:257`).
7. Sink: `if_ethersubr.c:1494` `M_ASSERTPKTHDR(m)` = KASSERT "invalid mbuf or
   no mbuf packet header" on INVARIANTS kernels; on production GENERIC the
   KASSERT is compiled out and `if_ethersubr.c:1502` dereferences
   `m->m_len` with `m == NULL` → fatal trap 12 (page fault at vaddr
   ~offsetof(struct mbuf, m_len)).

## Root cause: a dropped guard vs. the ancestor driver

sln(4) is a 2008 DragonFly port of the rl(4) ring-DMA RX model (the SC92031
is an 8139 clone — see the "Rsltek 8139D" probe entry at `if_sln.c:84-85`,
and the identical wrap-around `m_devget` + `m_copyback` idiom at
`if_rl.c:1062-1086`). The ancestor **guards** the same call:

```c
/* if_rl.c:1095-1099 */
		if (m == NULL)
			continue;

		IFNET_STAT_INC(ifp, ipackets, 1);

		ifp->if_input(ifp, m, NULL, -1);
```

The sln port dropped the `if (m == NULL) continue;` guard. (Note for the fix:
a literal `continue` is wrong in the sln loop shape — the `rx_offset` advance
at `if_sln.c:951` is inside the loop body after this point, so the fix must
guard only the `if_input` call; see fix.diff.)

## Reproduction (guest, X86_64_GENERIC, 6.5-DEVELOPMENT)

No SC92031 NIC exists in the QEMU guest (pciconf -l), so the *in-driver*
precondition (cluster exhaustion at RX-interrupt time) cannot be staged.
The **faulting statement itself** was therefore replicated verbatim: a KLD
module (`dfrnull.c`) creates a transient IFF_UP ethernet interface and issues
the exact statement of `if_sln.c:949` with the exact faulting argument
(`m = NULL`, the `m_devget()` failure value):

```c
dfrn_ifp->if_input(dfrn_ifp, NULL, NULL, -1);   /* == if_sln.c:949 with m==NULL */
```

Result on the running guest kernel (serial console, panic.txt):

```
DF-4432 replica: if_sln.c:949 -> if_input(ifp, NULL, NULL, -1) on dfrn0 (UP)
panic: ether_input: invalid mbuf or no mbuf packet header!
cpuid = 0
ether_input() at ether_input+0x197
dfrn_modevent() at dfrn_modevent+0xd0
module_register_init() at module_register_init+0x49
Debugger("panic")
```

The kernel died inside kldload; ssh never returned; `vm.sh status` → down.
Guest restored clean (`vm.sh reset with-src`) afterwards.

## Threat model

- **Who:** any local user. The RX interrupt fires on any incoming frame
  (broadcast noise qualifies); `m_devget()`'s M_NOWAIT allocation fails under
  kernel cluster/mbuf pressure, which local users can induce with ordinary
  socket workloads. On multi-user or server boxes with an sln(4) NIC, a
  memory-pressure spray + background traffic = recurring, unattended panics.
- **Impact:** NULL-deref panic (A:C of C/I/A only) — kernel DoS, no
  information disclosure, no control-flow hijack (fixed NULL address).

## Why `reproduced` and not `inconclusive`

The PoC executes the exact faulty statement with the exact faulty argument on
the exact production kernel build and produces the exact predicted panic
(the in-driver chain m_devget→NULL is code-certain by inspection at
uipc_mbuf.c:2247-2249). This matches the audit's precedent for
mechanism-proofs on hardware-less guests (cf. DF-3715 protocol-replica
module).

## Fix

`fix.diff` (git-apply-able, verified against this tree): guard the
`if_input` call on `m != NULL` while still advancing `rx_offset`.
Fix validation: **not testable on this guest** — exercising the fixed
`sln_rx` path requires SC92031 hardware (absent); the replica panics in
`ether_input`, which the (correct) fix never reaches with NULL.

## References

- sys/dev/netif/sln/if_sln.c:913-949 (bug), :915/:928 (NULL branch), :949 (sink call)
- sys/kern/uipc_mbuf.c:2235-2269 (m_devget NULL return)
- sys/net/if_ethersubr.c:642 (if_input wiring), :1494/:1502 (panic site)
- sys/dev/netif/rl/if_rl.c:1095-1099 (the dropped guard)

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