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

## Recommended fix (compile-validated)
`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
