DF-3418 / verdict.json
{ "finding_id": "DF-3418", "status": "reproduced", "reproduced": 1, "impact": "dos", "confidence": "certain", "verdict": "Reproduced on the stock INVARIANTS 6-cpu guest: rtpurelookup() in in6_ifdetach_dispatch() (sys/netinet6/in6_ifattach.c:844) takes a reference (sys/net/route.c:276) that is only dropped inside the `rt->rt_ifp == ifp` branch (in6_ifattach.c:845-849). When the ff02::1%ifp lookup instead matches a foreign route (the installed IPv6 default route), the reference is leaked permanently; deleting that route afterwards leaves its rtentry+sockaddrs allocated forever. Measured with vmstat -m routetbl: 10x route add/delete alone = +0 objects; 10x tap create/destroy alone = +0 objects; 10x combined = +18 objects; 20x combined = +38 objects (~1.9 objects/cycle, stable across two runs). Unbounded repetition = unbounded kernel memory retention from routine interface detach + route churn (root-triggered; Low).", "exploit_chain": "", "evidence": [ "findings/poc/DF-3418/run.log โ decisive vmstat -m routetbl counts: control runs flat (258->258, 276->276), test runs grow (258->276, 278->316)", "findings/poc/DF-3418/README.md โ root cause, reproduce steps, observed table, fix", "sys/netinet6/in6_ifattach.c:844-849 โ lookup ref never dropped on the rt_ifp != ifp path", "sys/net/route.c:276 โ _rtlookup() increments rt_refcnt on every successful match", "sys/netinet/in.c:1462-1464 โ the IPv4 twin in_ifdetach loops udbinfo; no analogous leak there because its route purge uses different code paths" ], "kernel_refs": [ "sys/netinet6/in6_ifattach.c:844", "sys/netinet6/in6_ifattach.c:845-849", "sys/net/route.c:276", "sys/net/route.c:286-293" ], "poc_changes": "No seed PoC existed; authored from scratch: pure shell orchestration (route add/delete + ifconfig tap create/destroy) with vmstat -m routetbl as the observable, three-way A/B/C attribution.", "attempts": 2, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026\troot@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC\tx86_64", "runtime_sec": 180, "guest_dirty": 0, "build_cmd": "(no compile; shell-only PoC)", "run_cmd": "sh findings/poc/DF-3418/run.sh (as root on the guest; see run.log for the exact executed commands)", "code_hash": "", "notes": "Leak is permanent per interface-detach-with-foreign-route event; materializes as memory retention when the pinned route is later deleted (DHCPv6/PPP default-route churn). Low severity: requires root to trigger detach/route ops; impact is slow unbounded kernel memory growth. DF-3417 pack (UDP6 purge-gap candidate) kept on disk as the honest false-positive record: udp6_attach hardcodes udbinfo[0] (udp6_usrreq.c) so udbinfo[0]-only purge is currently complete for UDP6.", "recommended_fix": "Drop the rtpurelookup() reference on all non-NULL paths (rtfree(rt) when rt->rt_ifp != ifp); see fix.diff", "fix_status": "not_testable", "fix_kernel_uname": "", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff authored against the read-only sys/ tree (reference dropped on the foreign-route path via rtfree); kernel rebuild validation not performed in this run (leak-class finding; mechanical fix). The leak is fully explained by the missing rtfree and the measured growth disappears by construction once the reference is dropped.", "fix_evidence": "findings/poc/DF-3418/fix.diff" } |