DF-0195 / verdict.json
{ "finding_id": "DF-0195", "status": "reproduced", "reproduced": 1, "impact": "leak", "confidence": "certain", "verdict": "Re-verified end-to-end on the stock INVARIANTS guest: an unprivileged user hammering the world-readable kern.devstat.all sysctl while a (root-simulated) device detach churns the unlocked device_statq receives freed-slab contents copied out verbatim -- live kernel heap freelist pointers at struct offset 0, 0xdeadc0de poison runs, stale residue (6 anomalies/45s) -- and a second run panicked the kernel from the unprivileged reader (Fatal trap 9, sysctl_devstat+0xa4 movq (%rbx),%rbx, proc 1118). Read-only primitive: no write path exists in the handler and every detach trigger on a stock system is privileged/physical, so the ceiling is heap-address disclosure + unpriv-triggerable panic during legit admin/hotplug activity -- High as filed, not a self-service uid0 chain. fix.diff (lockmgr EXCL on add/remove, SHARED around the sysctl walk) rebuilt in-guest (nativekernel rc=0) and re-running the exact PoC twice under live churn (generation>121k) yields 0 anomalies, no panic, guest up.", "exploit_chain": "unpriv reader sysctl(kern.devstat.all) races root/hotplug detach: kfree poisons chunk[0..63]=0xdeadc0de then writes zone freelist c_Next at offset 0 over dev_links.stqe_next; sysctl_devstat walk (subr_devstat.c:289-292) chases that pointer through freed chunks and SYSCTL_OUTs each 200-byte chunk -> kernel heap pointers + stale data disclosed to unprivileged userspace; when sampled in the poison-only window the chased value is non-canonical 0xdeadc0dedeadc0de -> GPF panic attributed to the unprivileged reader's process. uid0 BLOCKED: handler is copy-out only (no write primitive) and no unprivileged detach trigger exists on a stock system (mdconfig/camcontrol/dm/ccd/vn/kldload are privileged; USB/CAM hotplug is physical).", "evidence": [ "run.log: full unpriv reader log, 6 ANOMALY hexdumps incl. consecutive chunks 0xffff810117e22550/e227c0/e22890 (walker walked the freelist)", "panic.txt: Fatal trap 9 GPF 'Stopped at sysctl_devstat+0xa4: movq (%rbx),%rbx', current process 1118 (unpriv reader), from this run's serial console", "leak_sample.txt: extracted hexdumps showing heap ptr at offset 0 + 0xdeadc0de runs at 8..63", "fix_run.log: fixed kernel, two 45s races under live churn (generation 121374): 0 anomalies, RC=142, guest up", "fix_build.log: patch 8/8 hunks + make -j6 nativekernel rc=0, booted kernel #1 2026-09-04T04:11:33Z", "run_summary.txt: run matrix baseline/A/B/C/D" ], "kernel_refs": [ "sys/kern/subr_devstat.c:45", "sys/kern/subr_devstat.c:67-82", "sys/kern/subr_devstat.c:147-153", "sys/kern/subr_devstat.c:284-292", "sys/kern/kern_slaballoc.c:1557-1586", "sys/sys/queue.h:319-331" ], "poc_changes": "none to sources (seed reader.c/ds195_harness.c built and ran as-is); procedure only: reader must run in a blocking ssh session -- nohup-backgrounded readers get SIGHUP'd when the ssh session drops", "attempts": 4, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (baseline/race runs); fixed kernel #1: Fri Sep 4 04:11:33 UTC 2026", "runtime_sec": 188, "guest_dirty": 0, "build_cmd": "vm.sh run_user 'cc -O2 -o ~/reader ~/reader.c' ; vm.sh run_root 'cd /root/df195 && make' ; fix: vm.sh run_root 'cd /usr/src && patch < /root/fix.diff && make -j6 nativekernel KERNCONF=X86_64_GENERIC && make installkernel'", "run_cmd": "vm.sh run_root 'sysctl -w debug.use_weird_array=1 && kldload /root/df195/ds195.ko' ; vm.sh run_user '/home/maxx/reader 45'", "code_hash": "031a6b4352111f293de5d7395367183d842579f0c0e7fdd03df9dd02bbab8b05 (reader.c); e36d8be1b1c090f535af49cf0674f33aa176baa2e7e75903ec8639b0dfd8deb2 (ds195_harness.c); 81bebb83e963e66f60e97282e54a7be38810fb7004500f2f12857668feb4c478 (fix.diff)", "notes": "Pass-2 re-verification (prior seeded pack had no poc_results row). Baseline 0 anomalies proves detector noise-free. Panic reproduced on 2nd race attempt; leak on 1st. Guest reset with-src after fix validation (clean for next spawn). Reserved IDs DF-2955..DF-2960 unused: no new findings this pass.", "recommended_fix": "Add a global lock (lockmgr) taken exclusive in devstat_add_entry/devstat_remove_entry and shared around the sysctl_devstat walk, exactly as in fix.diff", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1: Fri Sep 4 04:11:33 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff applied cleanly (8/8 hunks), nativekernel rc=0 under -Werror, installed and booted (kernel #1). Exact PoC re-run twice (45s each) with harness loaded and churning (kern.devstat.generation=121374, ds195.ko in kldstat) and debug.use_weird_array=1: zero anomalies, zero panics, guest stayed up -- vs 6 leak anomalies + Fatal trap 9 panic on stock. SYSINIT lock-init ordering verified: SI_SUB_CREATE_INIT (0x2300000) precedes first devstat_add_entry at SI_SUB_DRIVERS (0x2400000) per sys/sys/kernel.h:183-187.", "fix_evidence": "fix_build.log (build rc=0, install rc=0); fix_run.log (0 anomalies x2, RC=142); env.txt (fixed-kernel uname + MD5)" } |