DF-3389 / verdict.json
{ "finding_id": "DF-3389", "status": "reproduced", "reproduced": 1, "impact": "dos", "confidence": "certain", "verdict": "Reproduced as a natural race on the stock-config INVARIANTS guest: ip6_setdstifaddr (ip6_input.c:796-805) stores an uncounted in6_ifaddr pointer into the PACKET_TAG_IPV6_INPUT m_tag; the terminal-header defer (ip6_input.c:739-755) hands the packet to transport6_processing_handler on netisr-0, whose consumers dereference ia6_flags (tcp_input.c:624-625, 1069-1076) after the address purge freed the object. A poison canary in ifac_free (0xDF3389A6 written to ia6_flags of every freed in6_ifaddr plus a pointer ring, checked on every deferred packet) confirmed 4 UAF reads across two independent 300 s full-rate races (683k and 824k add/delete cycles; 1.7-1.8M SYNs each; unpriv user traffic vs root purge). Controls: static address for 240 s gave 18,044,945 captured-and-deferred packets and ZERO hits; throttled cycling (~508 c/s) gave zero hits. Impact ceiling is honest and limited: a read of freed heap memory driving a branch (spurious ICMPv6 unreachable / deprecated-address RST, i.e. connection-level misbehavior) with panic potential under slab reuse or INVARIANTS-adjacent container states; no write primitive and no value reaches userspace, so no escalation path. Fix (fix.diff) replaces the pointer with a u_int32_t ia6_flags snapshot taken at store time and switches all three consumers to the snapshot; the fixed kernel was built clean and survived the identical race shape twice with zero anomalies while IPv6 TCP/ICMPv6 behaved normally. Unrelated bug observed during verification: full-rate SIOCDIFADDR_IN6 cycling panics stock kernels in nd6_timer_dispatch (rt_llinfo != ln) - separate finding recommended.", "exploit_chain": "", "evidence": "logs/run1-race-output.txt (3 UAF-HITs, counters 3/1523/1523), logs/run3-dmesg.txt (1 UAF-HIT ia6=0xfffff8004f31bde0 on fresh boot), logs/control-staticaddr.txt (18,044,945 defers, 0 hits), logs/fix-validation.txt (fixed kernel: 505k cycles + 1.06M SYNs race clean, guest up), df3389_canary.diff (poison instrumentation), fix.diff (validated fix), env.txt", "kernel_refs": [ "sys/netinet6/ip6_input.c:490", "sys/netinet6/ip6_input.c:534-536", "sys/netinet6/ip6_input.c:739-755", "sys/netinet6/ip6_input.c:776-790", "sys/netinet6/ip6_input.c:796-805", "sys/netinet6/ip6_var.h:271", "sys/netinet/tcp_input.c:624-629", "sys/netinet/tcp_input.c:1069-1076", "sys/netinet6/in6.c:2304-2353", "sys/netinet6/in6.c:1290-1337", "sys/net/if.c:3466-3491", "sys/net/if.c:3520-3590", "sys/net/route.c:285-309", "sys/net/netisr2.h:84-128" ], "poc_changes": "Harness written from scratch (no seed pack existed). flooder.c: batched nonblocking connects (blocking connect stalls ~75 s when the address flaps mid-SYN). cycler.c: direct SIOCAIFADDR_IN6/SIOCDIFADDR_IN6 ioctls (fork/exec ifconfig too slow), infinite lifetimes to silence per-add syslog. stretch.c: RFC3542 HBH/DSTOPTS attach is EPERM for unpriv on DragonFly - stretcher degraded to plain UDPv6 (still exercises store+defer). Canary kernel required two compile fixes vs the draft (-Wmissing-prototypes forward decl, -Wnested-externs file-scope extern).", "attempts": 8, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1: Tue Oct 20 19:50:24 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (canary) / #1: Tue Oct 20 20:35:08 UTC 2026 (fixed); stock #0 Thu Jul 2 06:02:54 UTC 2026", "runtime_sec": 7800, "guest_dirty": 0, "build_cmd": "guest: cc -O2 -o cycler cycler.c && cc -O2 -o flooder flooder.c && cc -O2 -o stretch stretch.c; kernel: cd /usr/src && patch -p1 < df3389_canary.diff && make -j6 nativekernel KERNCONF=X86_64_GENERIC && make installkernel", "run_cmd": "guest root: sysctl net.inet6.ip6.dad_count=0 && sh /root/df3389/race.sh 300 3 && sysctl net.inet6.ip6.df3389_hits && dmesg | grep UAF-HIT", "code_hash": "9ae2d96e7062bd3f8c1172e69f57364584007877ad6d5832d5c0c343f5d96e14", "notes": "Hit rate honest: 3 hits in run 1 and 1 in run 3 (full-rate ~2.3-2.7k cycles/s); 0 at ~508 c/s in 300 s; 0 in 18M-exposure static control. The nd6_timer rt_llinfo!=ln panic from full-rate cycling (stock and canary kernels) is a SEPARATE root-triggered bug; evidence captured, own finding recommended. Guest reset to clean with-src snapshot after validation.", "recommended_fix": "Snapshot ia6_flags into struct ip6aux at ip6_setdstifaddr time instead of storing the in6_ifaddr pointer; consumers use ip6_getdstifaddr_flags() (fix.diff, build- and race-validated)." } |