DF-3377 / verdict.json
{ "finding_id": "DF-3377", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "One unauthenticated IPv6 proto-103 PIM REGISTER packet panics any kernel whose routing daemon has configured a register mif: pim6_input() re-injects the decapsulated packet through if_simloop(multicast_register_if) (ip6_mroute.c:1842) and the fake register ifnet is never if_attach()ed (add_m6if:585-596 sets only if_xname/IFF_LOOPBACK/if_index), so if_simloops IFNET_STAT_INC writes through NULL if_data_pcpu (if_loop.c:284, if_var.h:685-688) -> Fatal trap 12, fault VA 0x0, Stopped at if_simloop+0x112: addq $0x1,(%rax) on stock kernel #0. Two further NULL derefs stack behind it (in6_ifawithifp via ip6_input.c:531-534 over NULL if_addrheads[]; in6_addr2zoneid via ip6_mdq:1420-1421 over NULL scope6_id) - the second was hit on the first fix iteration, the third is guarded the same way the output side already is (ip6_mroute.c:1433-1435). Distinct from DF-0423 (which needs a deleted register mif): this fires during normal RP operation with a valid mif and needs no MFC match. Write primitive is +=1 at VA 0x0 (unmapped, value/address not attacker-controlled) - DoS only, no escalation. Fix (allocate if_data_pcpu + if_addrheads[] in add_m6if, input-side register scope guard in ip6_mdq) validated on rebuilt kernel #1: both scoped variants traverse the full decap->mforward->mdq path (counters incremented), no panic.", "exploit_chain": "root daemon: MRT6_INIT(8-byte) -> MRT6_ADD_MIF(0,MIFF_REGISTER) -> MRT6_ADD_MFC(parent=0); attacker: 1x IPv6/PIM REGISTER (ver2 type1, non-null, valid cksum, inner=valid ip6 hdr w/ multicast dst) -> pim6_input -> if_simloop(multicast_register_if) -> IFNET_STAT_INC writes NULL+percpu -> supervisor write VA 0x0 -> panic. No uid0 route: page 0 unmapped, write value fixed +1.", "evidence": [ "run.log (baseline run cut at sendto by the panic)", "panic.txt (Fatal trap 12, Stopped at if_simloop+0x112, fault VA 0x0)", "panic.2.txt (second-layer crash in6_ifawithifp+0x35 on partial fix)", "fix_run.log (kernel #1: both variants forwarded, counters mif0 in=2, sg pkt=1+1, no panic observed)", "VERDICT.md (full 3-layer chain with path:line)" ], "kernel_refs": [ "sys/netinet6/ip6_mroute.c:1842", "sys/netinet6/ip6_mroute.c:585-596", "sys/netinet6/ip6_mroute.c:1420-1421", "sys/netinet6/ip6_mroute.c:1433-1435", "sys/net/if_loop.c:284", "sys/net/if_var.h:685-688", "sys/net/if.c:550-553", "sys/net/if.c:591-593", "sys/netinet6/ip6_input.c:531-534", "sys/netinet6/scope6.c:278-288" ], "poc_changes": "v1 did not panic (single uninstrumented variant); v2 added sys/param.h (NBBY), local SIOCGETSGCNT/SIOCGETMIFCNT defines, counter/upcall probes, second scoped variant, hand-computed PIM REGISTER checksum (pseudo-header len=PIM_MINLEN nxt=103), 8-byte MRT6_INIT arg (DF-3378). Crash site moved two layers earlier than the seeds scope6 prediction - documented.", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (baseline) / #1: Fri Sep 11 20:41:14 UTC 2026 (fix)", "runtime_sec": 2820, "guest_dirty": 0, "build_cmd": "cc -O -Wall -o /tmp/df3377 /root/poc/trigger_pim6_reg.c", "run_cmd": "/tmp/df3377", "code_hash": "516101d0f0edd47856325848086248ea2663ccb9da3831edb8e5c6e9adf061c3", "notes": "Guest left clean via vm.sh reset with-src. v6 file compiled unconditionally with inet6 (sys/conf/files:1850) - no custom kernel needed, stock X86_64_GENERIC. The crash needs no MFC match (re-injection panics before forwarding lookup). pim6_input itself parses lengths correctly (PIM_MINLEN/PIM6_REG_MINLEN checks verified).", "recommended_fix": "Allocate if_data_pcpu and if_addrheads[] for multicast_register_if in add_m6if (mirror if_attach) and skip the input-side in6_addr2zoneid for register-parent routes in ip6_mdq (mirror the existing output-side skip).", "fix_status": "fixed", "fix_kernel_uname": "DragonFly 6.5-DEVELOPMENT #1: Fri Sep 11 20:41:14 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff applied to guest /usr/src, X86_64_GENERIC rebuilt+installed+rebooted; identical PoC run completes both variants through the full register-decap forwarding path with counters incremented and no panic; baseline panic signature gone. First fix iteration (if_data_pcpu only) still crashed at in6_ifawithifp, proving layer 2 - final diff allocates both arrays.", "fix_evidence": "fix_run.log (kernel #1 clean run), fix_build.log (build summary), panic.2.txt (intermediate-layer crash proving the depth of the missing-initialization problem)" } |