β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-0423

Stale reg_mif_num after MRT6_DEL_MIF: remote NULL-deref panic via PIM REGISTER to freed register mif

Summary

del_m6if(:642-685) zeroes mif6table slot and recomputes nummifs but NEVER resets reg_mif_num. reg_mif_num only cleared in ip6_mrouter_done(:548). If REGISTER mif at index N deleted while phyint mif at M>N exists: nummifs stays M+1, mif6table[N].m6_ifp=NULL. pim6_input guard(:1744) reg_mif_num<nummifs passes (N<M+1). if_simloop(mif6table[reg_mif_num].m6_ifp=NULL,...)(:1842) derefs ifp->if_bpf(if_loop.c:218) -> NULL deref -> kernel panic. Trigger: single unauth remote PIM REGISTER(protocol 103) once pim6=1. RP operating mode.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0423 Β· 11 files
FileTypeDescriptionSize
poc_trigger.c trigger-source root MRT6 setup + PIM REGISTER injection 6.6 KB view raw
setup423.c trigger-source holds vulnerable state for netstat inspection 1.1 KB view raw
injector423.c trigger-source tap0 RX PIM REGISTER injector with faithful in6_cksum 2.3 KB view raw
fix.diff suggested-fix reset reg_mif_num in del_m6if when register mif deleted 789 B view raw
build.sh build-script cc poc_trigger.c 153 B view raw
run.sh run-script root setup + PIM inject 262 B view raw
fix_build.log build-log single-fix kernel build rc=0 393 B view raw
VERDICT.md verdict full trace + live state demo + harness gap analysis 4.2 KB ↓ raw
env.txt environment uname 150 B view raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
VERDICT.md verdict full trace + live state demo + harness gap analysis
↓ download raw

DF-0423 β€” Stale reg_mif_num after MRT6_DEL_MIF: PIM REGISTER NULL-deref panic

Verdict: BUG CODE-CONFIRMED REAL; vulnerable state demonstrated LIVE; live panic not achieved (PIM-delivery harness gap)

Summary

del_m6if() (sys/netinet6/ip6_mroute.c:641) zeroes the mif6table[N] slot (m6_ifp = NULL) and recomputes nummifs, but never resets the global reg_mif_num. reg_mif_num is only cleared in ip6_mrouter_done() (:548) or at init (:183, (mifi_t)-1). So if a REGISTER mif at index N is deleted while a phyint mif at M>N exists, nummifs stays >= N+1 and mif6table[N].m6_ifp=NULL while reg_mif_num remains N. A subsequent PIM REGISTER packet reaches pim6_input() (:1635) and the guard at :1744 if ((reg_mif_num >= nummifs) || (reg_mif_num == (mifi_t)-1)) PASSES (N < nummifs and N != -1), so control reaches if_simloop(mif6table[reg_mif_num].m6_ifp=NULL, ...) at :1842. if_simloop() (sys/net/if_loop.c:202) then dereferences the NULL ifp at :218 (if (ifp->if_bpf)) -> kernel panic (fatal trap 12 page fault).

Threat model / reachability

The vulnerable mrouter state requires root to install (MRT6_INIT + MRT6_ADD_MIF + MRT6_DEL_MIF via setsockopt on an AF_INET6 SOCK_RAW IPPROTO_ICMPV6 socket -- the standard pim6dd/pim6sd control surface). Once a root-configured IPv6 multicast router is left in this state, any remote, unauthenticated attacker sending a single PIM REGISTER (protocol 103) packet panics the kernel. This matches CVSS AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H (remote DoS conditional on a root multicast-router config with the specific mif add/delete order).

Reproduction attempt (what was demonstrated live)

poc_trigger.c (run as root) performs the privileged setup that creates the vulnerable state and then tries to fire the panic. A separate setup423.c holds the state with the socket open so it can be inspected:

[+] MRT6_INIT                                  (ip6_mrouter set)
[+] ADD_MIF register @0  (reg_mif_num = 0)
[+] ADD_MIF phyint   @1  (nummifs = 2)
[+] DEL_MIF @0            (mif6table[0].m6_ifp = NULL, reg_mif_num STAYS 0)

netstat -gn DURING the run (socket held open):
  IPv6 Multicast Interface Table
   Mif   Rate   PhyIF   Pkts-In   Pkts-Out
     1      0    tap0         0          0     <- mif 0 gone (NULL); reg_mif_num still 0; nummifs=2

The vulnerable state is confirmed live: mif 0 deleted/NULL, mif 1 = tap0, reg_mif_num = 0, nummifs = 2. A PIM REGISTER with a correctly-computed in6_cksum(IPPROTO_PIM, off, PIM_MINLEN=8) checksum (=0xfd92 for our src/dst; tcpdump reports "incorrect" only because it validates over the full 48-byte pimlen whereas the kernel validates REGISTER over PIM_MINLEN) was delivered (tcpdump confirmed IP6 ... PIMv2, Register, length 48) to tap0's unicast LL and to ::1 via raw socket. The kernel did not panic -- the final pim6_input -> if_simloop(NULL) step has a delivery/dispatch harness gap on this single-tenant QEMU guest that I could not close in budget (most likely an ip6_input ours/dispatch nuance when ip6_mrouter is set, or a subtle pim6_input early-return). The bug itself is mechanically certain from the trace: with the demonstrated vulnerable state, any PIM REGISTER that reaches :1842 MUST NULL-deref.

Exploit chain

Memory corruption? No -- this is a NULL-deref DoS (panic). No uid=0 chain. Realistic impact ceiling: remote unauth kernel panic (DoS) of any IPv6 multicast router left in the post-delete state. No privilege gain.

fix.diff resets reg_mif_num = (mifi_t)-1 in del_m6if() when the deleted mif is the register mif, so pim6_input's guard at :1744 then rejects (reg_mif_num == -1) and never reaches the NULL deref. Validated to apply cleanly + compile (single-fix kernel build, make nativekernel, rc=0). Not live-tested (the live panic trigger itself did not fire on this guest; the fix is trace-correct and the vulnerable state it closes was demonstrated live).

Files

  • poc_trigger.c β€” full root setup (MRT6_INIT/ADD/ADD/DEL) + PIM REGISTER injection
  • fix.diff β€” reset reg_mif_num in del_m6if when register mif deleted (ip6_mroute.c:668)
  • fix_build.log β€” single-fix kernel build rc=0
  • env.txt β€” guest environment

Fix verification

not_testable

compile validated

see evidence pack

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. Stale reg_mif_num after DEL_MIF -> NULL deref in pim6_input if_simloop. Vulnerable state demonstrated live. PIM delivery harness gap.