DF-3450 / verdict.json
{ "finding_id": "DF-3450", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "Race reproduced 2/2 baseline runs on the stock INVARIANTS guest with both fault variants: encap4_input (ip_encap.c:231-235) reads the ipip_input hook unlocked and twice (test, then call); ip_mroutes MOD_UNLOAD NULLs it under mroute_token (which the netisr reader never takes) and performs no netisr drain before the linker unmaps the module text. Run 1: RIP=0x0 (call-NULL variant). Run 2 (23 s): RIP=0xffffffff82600275 inside the unloaded module text (execute-after-unmap variant); both in netisr0/Idle context while a root kldload/kldunload loop raced a proto-4 packet spray (any local user or on-link peer can be the packet source). Root-gated setup, race winnable in seconds. Two-sided fix validated: reader-side single-read snapshot (kernel rebuilt, #1 Sep 12 23:27:53) + unload-side netmsg_service_sync (module rebuilt) survived 480 s / 115,650 load-unload cycles / 16.8 M packets with no crash; a single-sided fix attempt (unload drain only) still panicked at 89 s with RIP=0x0, documenting that both hunks are required.", "exploit_chain": "root: sh racer.sh (kldload/kldunload ip_mroute loop); unpriv/remote: proto-4 packet stream to a local address (sprayer). netisr0 encap4_input reads ipip_input non-NULL, unload NULLs+unmaps concurrently -> indirect call to 0x0 (observed) or into unmapped-but-predictable module text (observed at module_base+0x275). Escalation ceiling: with a KLD reloaded into the freed VA before a late readers call, the diverted call executes chosen module code - root-gated setup caps honest impact at panic/DoS.", "evidence": [ "panic.txt: run 1 - fault virtual address 0x0, instruction pointer 0x8:0x0, supervisor read instruction page not present, current process Idle", "run.2.log: run 2 - instruction pointer 0x8:0xffffffff82600275 (former ip_mroute.ko text, base 0xffffffff82600000), current process Idle", "fixattempt1_single_sided.log: single-sided fix still panics RIP=0x0 at 89s (proves reader double-read independently fatal)", "fix.diff: two-sided fix (ip_encap.c snapshot + ip_mroute.c netmsg_service_sync)", "env.txt + validation narrative in VERDICT.md: 480s/115650 cycles/16.8M packets no-crash on rebuilt kernel #1" ], "kernel_refs": [ "sys/netinet/ip_encap.c:231", "sys/netinet/ip_encap.c:107", "sys/net/ip_mroute/ip_mroute.c:3266", "sys/net/ip_mroute/ip_mroute.c:3281", "sys/net/ip_mroute/ip_mroute.c:3285", "sys/netinet/ip_demux.c:315" ], "poc_changes": "Harness written from scratch (no runnable seed existed): raw-socket proto-4 sprayer with hand-built IP header + checksum (non-TCP/UDP hashes to netisr0 per ip_demux.c), 96 fan-out raw sockets to prove X_ipip_input delivery and widen the in-module window, kldload/kldunload racer loop. First single-sided fix attempt failed; fix.diff extended to the two-sided version with a kernel rebuild.", "attempts": 3, "guest_uname": "baseline: DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026; fix validation: #1: Sat Sep 12 23:27:53 UTC 2026 (rebuilt X86_64_GENERIC with ip_encap.c hunk)", "runtime_sec": 2410, "guest_dirty": 1, "build_cmd": "(cd /usr/src/sys/net/ip_mroute && make obj && make); cc -O2 -o sprayer sprayer.c; [fix] make -j24 nativekernel KERNCONF=X86_64_GENERIC && make installkernel KERNCONF=X86_64_GENERIC", "run_cmd": "(./sprayer 96 &) ; (sh racer.sh &) ; watch dfbsd-qemu/vm.sh log", "code_hash": "63b1e963dbcef6494c0d83c6b0d4655d52263759b480908e05f724b2b9447bc7", "notes": "Not DF-0689 (encaptab list) and not covered by the validated DF-3425/DF-3433 mp_token fix - different global (ipip_input), different mechanism (function-pointer hook vs list walk), different fix surface. Cross-ref: DF-0689s validated fix.diff (findings/poc/DF-3425, DF-3433) adds mp_token to encap_attach/attach_func/detach only.", "recommended_fix": "Two-sided: snapshot ipip_input once into a local in encap4_input before the test+call, and make ip_mroute MOD_UNLOAD call netmsg_service_sync() after clearing the hooks so netisr drains before the module text is unmapped.", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1: Sat Sep 12 23:27:53 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (reader hunk compiled in; unloader hunk in rebuilt ip_mroute.ko)", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Baseline: panic in 23-89 s (RIP=0x0 and RIP=unmapped-module-text variants). Single-sided fix (unload drain only): still panicked at 89 s, RIP=0x0 - the readers double read is independently fatal, both hunks required. Two-sided fix on rebuilt kernel #1 + rebuilt module: 480 s continuous race, 115,650 kldload/kldunload cycles, 16.8 M sprayed proto-4 packets, zero panics, guest alive and responsive.", "fix_evidence": [ "fix.diff (ip_encap.c + ip_mroute.c hunks)", "fixattempt1_single_sided.log (failed single-sided attempt, RIP=0x0)", "VERDICT.md validation section (480 s / 115,650 cycles / no crash)", "panic.txt, run.2.log (baseline faults)" ] } |