# DF-3377 — PIM6 REGISTER decap panics: fake `multicast_register_if` is never attached; `if_simloop()` writes through NULL `if_data_pcpu`

## What

`pim6_input()` re-injects every decapsulated PIM REGISTER packet through
`if_simloop(mif6table[reg_mif_num].m6_ifp, ...)` (sys/netinet6/ip6_mroute.c:1842).
For a register mif that ifnet is the static `struct ifnet multicast_register_if`
(ip6_mroute.c:175), which `add_m6if()` "initializes" with only three fields —
`if_xname`, `if_flags |= IFF_LOOPBACK`, `if_index` (ip6_mroute.c:585-596) — it is
**never `if_attach()`ed**. `if_simloop()` bumps per-CPU ifnet statistics via
`IFNET_STAT_INC(ifp, ipackets, 1)` = `(ifp)->if_data_pcpu[mycpuid].ifd_ipackets += 1`
(sys/net/if_loop.c:284, sys/net/if_var.h:685-688). `if_data_pcpu` is allocated by
`if_attach()` (sys/net/if.c:591-593) — for the fake ifnet it is **NULL** →
supervisor write to VA 0x0 → kernel panic.

A second latent layer sits behind it: even with stats fixed, `ip6_mdq()` calls
`in6_addr2zoneid(ifp, ...)` on the register ifnet (ip6_mroute.c:1420-1421);
`SID(ifp)` is NULL (no `if_afdata[AF_INET6]`) and scope6.c:278-288 derefs
`sid->s6id_list[scope]` for any non-global-scope src/dst → second NULL panic.
The output side of the same function already skips the scope check for register
interfaces (ip6_mroute.c:1433-1435) — the input side forgot.

## Who can trigger it

* Setup (root, normal PIM-SM RP operation): MRT6_INIT, MRT6_ADD_MIF with
  MIFF_REGISTER, MRT6_ADD_MFC with the register mif as parent.
* Trigger: **one unauthenticated PIM REGISTER packet** (IPv6 proto 103) from
  any host that can reach the RP. No PIM protocol state is needed — the inner
  packet need only be a valid IPv6 header with multicast dst; with the layer-1
  crash the inner (S,G) does not even need to match an MFC entry (the panic is
  in the re-injection, before any forwarding lookup).

## Reproduce

```
# on the guest (root):
cc -O -Wall -o /tmp/df3377 /root/poc/trigger_pim6_reg.c
/tmp/df3377
# expected (stock kernel #0):
#   [setup] ... rc=0 ...
#   [trigger] variant A ... sendto: rc=52
#   -> Fatal trap 12: page fault while in kernel mode
#      fault virtual address = 0x0
#      Stopped at if_simloop+0x112: addq $0x1,(%rax)
# expected (patched kernel): completes, "no panic observed: guest still alive"
```

The PoC injects the REGISTER locally (raw socket to ::1) standing in for a
remote sender; pim6_input processes it identically.

## Files

* `trigger_pim6_reg.c` — instrumented trigger + bisection probes
* `build.sh` / `run.sh`
* `run.log` — decisive run (stops mid-stream at the panic)
* `panic.txt` — serial console capture
* `fix.diff` — two-layer fix (per-CPU stats alloc + input-side scope guard)
* `VERDICT.md`, `manifest.json`, `verdict.json`
