DF-3407 / verdict.json
{ "finding_id": "DF-3407", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "REPRODUCED: stock kernel panicked in <=60s with tunclose -> if_clone_destroy -> if_clone_free_unit: bit is already cleared while racing open()/close() on /dev/tun against SIOCIFDESTROY. Root cause is a family of unsynchronized lifetime windows: tunclose clears TUN_OPEN at if_tun.c:317 then runs an unlocked body on the softc before its own if_clone_destroy at :352 (concurrent SIOCIFDESTROY passes the TUN_OPEN gate at :419 and kfrees the softc at :270 -> UAF); tunopen resurrects via tuncreate when si_drv1==NULL (:286-287) onto a cdev already in destroy_dev putaway (proved live: v1-patched kernel panicked at tunopen->tuncreate->reference_dev sysref_get SRF_PUTAWAY, panic.2.txt); and if_clone_destroy itself drops the ifnet lock between the ifunit lookup and if_clone_free_unit/ifc_destroy (sys/net/if_clone.c:110-132) so two destroyers double-run teardown (filed as framework component DF-3414). Fix validated in 3 iterations: v3 (if_tun.c ifnet_lock serialization + no-resurrect, plus if_clone.c lock held across lookup..teardown) survives 2x120s races (56521/59259 open-close cycles) with no panic vs baseline panic in <=60s. Both endpoints root-gated (if_tun.c:283, if.c:2008-2017) -> no unprivileged escalation possible; ceiling is root-triggered panic/UAF.", "exploit_chain": "root: fork opener(/dev/tun open/close loop; autoclone units) + destroyer(AF_ROUTE SIOCIFDESTROY on tun0..32) -> tuncloses unlocked tail races an external destroy -> softc freed under the running tunclose (UAF read/write) and/or both destroyers carry the same unit through if_clone_destroys unlocked lookup gap -> double clone-unit free + ifnet UAF -> INVARIANTS KKASSERT panic (non-INVARIANTS: silent UAF). Unprivileged escalation impossible: every entry is root-gated.", "evidence": [ "panic.txt \u2014 baseline panic: tunclose -> if_clone_destroy -> if_clone_free_unit: bit is already cleared", "panic.2.txt \u2014 v1-patched kernel: tunopen -> tuncreate -> reference_dev sysref_get SRF_PUTAWAY (resurrection window live)", "run.log \u2014 baseline race output (ssh died mid-run; guest panicked)", "run.fix.log / run.fix.2.log \u2014 v3 kernel: 2x120s races, 56521/59259 cycles, no panic", "fix.diff \u2014 v3: if_tun.c serialization + no-resurrect; if_clone.c lock across lookup..teardown", "VERDICT.md \u2014 full 3-iteration validation narrative" ], "kernel_refs": [ "sys/net/tun/if_tun.c:286-293", "sys/net/tun/if_tun.c:308-353", "sys/net/tun/if_tun.c:414-429", "sys/net/tun/if_tun.c:247-271", "sys/net/tun/if_tun.c:213-214", "sys/net/if_clone.c:59-95", "sys/net/if.c:2008-2017" ], "poc_changes": "PoC written fresh for this finding (no seed). Fixed a missing sys/wait.h include after the first build (warning only).", "attempts": 5, "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", "runtime_sec": 110, "guest_dirty": 1, "build_cmd": "cc -O2 -o /root/race_tun /root/race_tun.c", "run_cmd": "/root/race_tun 60", "code_hash": "ede8281d8ae4c514f0acd7732dcfdb33f27505e1d84cb247f580134f79b13579", "notes": "3 distinct panic signatures observed across baseline/v1/v2 (double clone-unit free; resurrection sysref panic; double free again via framework TOCTLU) -- the same missing-synchronization family. tap(4) DF-3405 is the twin and needs the same if_tun.c half; the if_clone.c half (DF-3414) affects every clonable interface. Guest reset with-src after validation.", "recommended_fix": "Take the ifnet lock (recursive sleepable mtx) around the tunopen/tunclose critical sections so the cdev paths agree with the ifconfig clone create/destroy paths that already hold it.", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Sat Sep 12 09:05:29 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "v3 fix (if_tun.c ifnet_lock around open/close critical sections + no-resurrect ENXIO; if_clone.c ifnet_lock held across lookup..free_unit..ifc_destroy) validated on the rebuilt guest kernel: baseline panicked <=60s; patched kernel survived 2x120s of the identical race (56521 and 59259 open/close cycles, exit 0, guest up). Intermediate v1/v2 failures (resurrection panic, framework TOCTOU panic) are documented in panic.2.txt and VERDICT.md and are covered by the v3 diff.", "fix_evidence": "run.fix.log; run.fix.2.log; fix.diff (v3, if_tun.c + if_clone.c); fix_kernel_info.txt; panic.2.txt (v1 intermediate)" } |