sln_rx() feeds a NULL mbuf to ifp->if_input() when m_devget() fails β guaranteed kernel panic (guard dropped from the rl(4) ancestor)
Summary
Both m_devget NULL branches only bump ierrors and fall through to the UNCONDITIONAL if_input - ether_input panics (M_ASSERTPKTHDR KASSERT on INVARIANTS, m->m_len NULL deref on production). The ancestor rl(4) guards this exact idiom; the 2008 sln port dropped it. VERIFIED on guest via replica KLD executing the exact statement: 'panic: ether_input: invalid mbuf or no mbuf packet header!' with backtrace. mbuf-pressure trigger with any RX traffic = recurring unattended panics. Guard-without-continue fix (rx_offset advance must still run).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4432 Β· 13 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.6 KB | β raw | |
| VERDICT.md | β | 5.2 KB | β raw | |
| dfrnull.c | β | 3.0 KB | view raw | |
| Makefile | β | 49 B | β download | |
| build.sh | β | 194 B | view raw | |
| run.sh | β | 128 B | view raw | |
| build.log | β | 859 B | view raw | |
| run.log | β | 1.1 KB | view raw | |
| panic.txt | β | 715 B | view raw | |
| env.txt | β | 529 B | view raw | |
| fix.diff | β | 535 B | view raw | |
| manifest.json | β | 1.2 KB | view raw | |
| verdict.json | β | 3.9 KB | view raw |
DF-4432 β sln_rx() passes a NULL mbuf to ifp->if_input (kernel panic)
File: sys/dev/netif/sln/if_sln.c
Class: NULL-pointer dereference / local DoS (panic) β memcorrupt-adjacent
Severity: Medium β CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:H
Build
dfbsd-qemu/vm.sh run_root "sh /tmp/df4432/build.sh"
Builds dfrnull.ko as an in-tree module in the guest (/usr/src/sys/modules/dfrnull).
Run
dfbsd-qemu/vm.sh run_root "/usr/src/sys/modules/dfrnull/run_root_step; kldload /usr/src/sys/modules/dfrnull/dfrnull.ko"
i.e. simply kldload /usr/src/sys/modules/dfrnull/dfrnull.ko as root on the guest.
Expected
Immediate kernel panic on the serial console:
Fatal trap 12: page fault while in kernel mode mp_lock=0x... cpuid=...; lapic.idx=... fault virtual address = 0x18 # offsetof(struct mbuf, m_len) ... ether_input+0x.. at ...
The module replicates the exact statement of sys/dev/netif/sln/if_sln.c:949
(ifp->if_input(ifp, m, NULL, -1);) with the exact faulting argument
(m == NULL, the m_devget() failure value from if_sln.c:915/928).
The ancestor driver rl(4) guards this with if (m == NULL) continue;
(if_rl.c:1095-1096) before its own ifp->if_input at if_rl.c:1099;
the sln(4) port dropped that guard.
No SC92031 hardware is needed for this replica: the faulting statement is reproduced verbatim. The in-driver trigger additionally requires cluster exhaustion at RX-interrupt time (m_devget M_NOWAIT failure) plus traffic on the sln interface β that half is code-certain by inspection, hardware-gated on the QEMU guest (no Silan NIC present).
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
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).- 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 atif_sln.c:913, normal branch atif_sln.c:926. m_devget()(sys/kern/uipc_mbuf.c:2235-2269) allocates withm_getl(len, M_NOWAIT, ...); on allocation failure it frees the partial chain and returns NULL (uipc_mbuf.c:2247-2249).if_sln.c:915-919/if_sln.c:928-937:if (m == NULL) { ierrors++; if_printf(...); }β and then falls through (nocontinue, no guard).if_sln.c:949:ifp->if_input(ifp, m, NULL, -1);β unconditional,m == NULLhere.ifp->if_input == ether_inputfor this interface (if_ethersubr.c:642insideether_ifattach, called fromif_sln.c:257).- Sink:
if_ethersubr.c:1494M_ASSERTPKTHDR(m)= KASSERT "invalid mbuf or no mbuf packet header" on INVARIANTS kernels; on production GENERIC the KASSERT is compiled out andif_ethersubr.c:1502dereferencesm->m_lenwithm == 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:
/* 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):
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.
Fix verification
not_testablefix.diff authored and verified git-apply-clean against sys/; patched-path validation impossible on this guest (no SC92031 NIC, so sln_rx cannot be driven); the baseline panic was reproduced and the fix removes the only NULL path into if_input on the sln RX path.
findings/poc/DF-4432/fix.diff (git apply --check clean); VERDICT.md 'Fix' section
Confirmed kernel references
Detail
Exploit chain
local user induces kernel mbuf/cluster pressure (ordinary socket spray) while any traffic arrives on the sln(4) interface -> RX interrupt -> sln_rx -> m_devget(M_NOWAIT) fails, returns NULL -> if_sln.c:949 calls if_input(ifp, NULL,...) -> ether_input KASSERT/NULL deref -> kernel panic (DoS). Proven on guest by replica module executing the exact statement; no uid0 route exists (fixed NULL address, panic-only).
Evidence (decisive lines)
panic.txt (serial console: 'DF-4432 replica...' then 'panic: ether_input: invalid mbuf or no mbuf packet header!', backtrace ether_input+0x197 <- dfrn_modevent+0xd0 <- module_register_init); run.log (ssh never returned; guest down after run); VERDICT.md (full chain); fix.diff (guard the if_input call, verified git-apply --check clean)
PoC changes
Seed-to-working: module had to be built in-tree (guest /usr/src/sys has no modules/ subtree in the snapshot; SYSDIR must be passed absolute or kern.fwd.mk's !=-glob evaluates from the wrong cwd); added
Verified recommended fix
Guard the if_input call on m != NULL in sln_rx (do NOT continue: the rx_offset advance at if_sln.c:951 must still run) - see findings/poc/DF-4432/fix.diff
Verdict
sln_rx() passes a NULL mbuf to ifp->if_input() when m_devget() fails (if_sln.c:915/928 NULL branches fall through to the unconditional call at if_sln.c:949); ether_input() panics (M_ASSERTPKTHDR KASSERT on INVARIANTS kernels, NULL m->m_len deref on production GENERIC, if_ethersubr.c:1494/1502). The ancestor driver rl(4) guards this exact pattern (if_rl.c:1095-1096 'if (m == NULL) continue;'); the sln(4) port dropped the guard. The faulting statement was replicated verbatim (if_input(ifp, NULL, NULL, -1)) on the guest production kernel via KLD module and panicked it inside kldload with the predicted backtrace (ether_input+0x197). The in-driver precondition (m_devget M_NOWAIT failure at RX-interrupt time = kernel cluster pressure + incoming traffic on sln0) is code-certain (uipc_mbuf.c:2247-2249) but hardware-gated on this guest (no SC92031). Kernel DoS: recurring unattended panics on any host with an sln(4) NIC under memory pressure.
No comments yet.