DF-5823 / verdict.json
{ "finding_id": "DF-5823", "status": "not_reproduced", "reproduced": false, "impact": "none", "confidence": "likely", "verdict": "The race is statically certain: sysctl_kern_procs KERN_PROC_PGRP/KERN_PROC_TTY pre-filters (sys/kern/kern_proc.c:1736-1747) dereference the p_pgrp->pg_session->s_ttyp->t_dev chain under only the allproc-bucket proc_token (shared), with no p->p_token and no pgref, while enterpgrp()/leavepgrp() swap p_pgrp and the final pgrel() pair frees the pgrp (kern_proc.c:643-690) with the member still scannable on allproc; the p->p_session == NULL guard at 1743 is dead code because p_session is a macro through p_pgrp (sys/sys/proc.h:344). Runtime: 17,090,388 filter-executing sysctl polls (~95k/s for 180s) against 24 churn workers doing setpgid(0,0)+setsid() plus controlling-tty acquisition produced no panic and no observable corruption - every hop of the freed chain lands in never-unmapped dedicated-zone memory (M_PGRP/M_SESSION), so the realistic outcome is a stale/garbage compare, not a fault. Race real, impact ceiling not reached on this guest.", "exploit_chain": "", "evidence": "VERDICT.md (mechanism + run narrative); run.log (full 180s run: scanner iterations: 17090388 (94942.5/s), guest up afterwards); df5823_churn.c (churn unit: setpgid(0,0) -> setsid() frees a pgrp while the proc is on allproc; ctty variant races s_ttyp hop)", "kernel_refs": [ "sys/kern/kern_proc.c:1736", "sys/kern/kern_proc.c:1741", "sys/kern/kern_proc.c:1743", "sys/kern/kern_proc.c:814", "sys/kern/kern_proc.c:859", "sys/kern/kern_proc.c:656", "sys/sys/proc.h:344" ], "poc_changes": "Authored from scratch (no seed). Churn unit chosen after establishing that zombies keep p_pgrp until reap (kern_exit.c:1271) so the on-list pgrp destruction must come from a live member doing setsid(), not from exit.", "attempts": 1, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly v6.5.0.1712.g89e6a-DEVELOPMENT #1: Mon Jun 29 14:18:01 UTC 2026 root@ephemeral-5c2002c44b6c:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 180, "guest_dirty": 0, "build_cmd": "cc -O2 -o df5823_churn df5823_churn.c", "run_cmd": "./df5823_churn 180", "code_hash": "94be4c7e81c844c06bc9863f54b3ae2fd15b50302145678d8446974813cc868e", "notes": "Guest remained healthy (uptime continuous, load 22 during churn). The rig kills its own process group at exit, which also kills the ssh-spawned shell - expected, not a crash. fix.diff authored (p_token shared across the two filter chains, explicit p_pgrp->pg_session walk replacing the dead p_session NULL check); not rebuild-validated since no memory corruption reproduced.", "recommended_fix": "Hold p->p_token shared (or pgref a p_pgrp snapshot) across the KERN_PROC_PGRP/KERN_PROC_TTY filter chain reads in sysctl_kern_proc.", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff authored but not kernel-build validated: runtime impact did not reproduce, so there is no observable to confirm flipped.", "fix_evidence": "fix.diff (p_token shared around both filter cases; dead p_session NULL check replaced with explicit p_pgrp->pg_session chain checks)" } |