DF-3420 / verdict.json
{ "finding_id": "DF-3420", "status": "not_reproduced", "reproduced": 0, "impact": "none", "confidence": "likely", "verdict": "Code-certain lifetime defect: pfi_update_status() bcopys the pf group ifg_members TAILQ head without the ifgroup lock (pf_if.c:722-724) and then walks that snapshot under LK_SHARED (pf_if.c:736-758); a concurrently removed+kfreed ifg_member (if.c:1224, under LK_EXCLUSIVE) that was head-of-list at snapshot time is dereferenced after free. Three adversarial stress rounds on the 6-cpu INVARIANTS guest (30k pfctl -si + 3k tap churn; 2M tight DIOCGETSTATUS + 8k churn; 3M tight DIOCGETSTATUS + 6k oldest-member churn targeting the head-entry pattern) totalled >5.3M status calls and never hit the few-instruction window; no panic, no corruption observed. Root-vs-root only (/dev/pf 0600, group ops are admin). Filed Low: hardening-scope lifetime bug, realistic impact negligible.", "exploit_chain": "", "evidence": [ "findings/poc/DF-3420/README.md \u2014 defect description, stress matrix (3 rounds, >5.3M iterations), proposed lock-scope fix", "findings/poc/DF-3420/setstatif.c \u2014 sets pf status-if to the group name so DIOCGETSTATUS exercises the group branch", "findings/poc/DF-3420/statloop.c \u2014 tight DIOCGETSTATUS loop used for rounds 2-3", "sys/net/pf/pf_if.c:722-724 \u2014 unlocked bcopy of ifg_members head", "sys/net/pf/pf_if.c:736-738 \u2014 walk of the snapshot under LK_SHARED", "sys/net/if.c:1186,1224 \u2014 membership insert/remove under LK_EXCLUSIVE, kfree of ifg_member" ], "kernel_refs": [ "sys/net/pf/pf_if.c:722-724", "sys/net/pf/pf_if.c:736-738", "sys/net/if.c:1224" ], "poc_changes": "Authored from scratch: setstatif.c (DIOCSETSTATUSIF to a group name), statloop.c (tight ioctl loop; first version used a guessed struct size and failed with Operation not supported - fixed by compiling against the guests /usr/src pfvar.h), churn scripts cycling tap create/destroy including the oldest-member/head-of-list pattern.", "attempts": 3, "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": 540, "guest_dirty": 0, "build_cmd": "cc -O -I/usr/src/sys -I/usr/src/sys/net -o /tmp/statloop /tmp/statloop.c", "run_cmd": "/tmp/setstatif tap && (/tmp/churn*.sh & /tmp/statloop 3000000 & wait)", "code_hash": "", "notes": "Stress logs: round outputs statloop done N / CHURN-DONE captured in /tmp/s3420{,b,c,d}.log on the guest during the run; guest was reset clean afterwards. Downgraded Medium->Low: window is the span between bcopy and lockmgr (nanoseconds), and both racing operations require root.", "recommended_fix": "Acquire ifgroup_lockmgr(LK_SHARED) before the bcopy of ifg_members and hold it through the walk; see fix sketch in README.md", "fix_status": "not_testable", "fix_kernel_uname": "", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "Baseline never reproduced, so fix validation is not meaningful; the lock-scope fix in README.md removes the unlocked snapshot by construction.", "fix_evidence": "" } |