DF-5125 / verdict.json
{ "finding_id": "DF-5125", "status": "reproduced", "verdict": "synchronize_rcu() in the DragonFly drm LinuxKPI expands to zero instructions (rcupdate.h:94; linux_rcu.c provides no implementation), so the canonical teardown (swap pointer; synchronize_rcu(); kfree(old)) frees objects immediately while readers on other CPUs are provably still inside their read-side critical sections. Reproduced on the 6-CPU QEMU guest with the shipped drm.ko shim: UAF detection in 4-5/5 rounds (10/10 in extra rounds), controlled content read through the freed object, a function pointer written into the reclaimed freed memory invoked by the kernel (canary x3), and a fatal trap with RIP = the attacker-written pointer value (0xffffffff52170000). In-tree callers include i915_gem.c:3713/3731. fix.diff (real synchronize_rcu + read-side accounting + grace deferral) drives all hits to 0 on the identical harness.", "reproduced": true, "impact": "panic", "confidence": "certain", "exploit_chain": "reader pinned cpu2: rcu_read_lock(); v=rcu_dereference(gp) [handshake proves pointer held mid-CS] -> writer cpu0: rcu_assign_pointer(gp,v2); synchronize_rcu() [compiles to nothing]; kfree(v1) -> same-cpu slab LIFO reclaim of v1 with controlled content (magic+fp) -> reader reads poisoned magic (UAF read) and calls v->v_fp (kernel indirect call through data in freed+reclaimed memory) -> with fp=0xffffffff52170000: Fatal trap 12, instruction pointer = 0xffffffff52170000 (arbitrary kernel RIP control from the UAF). Full uid=0 chain blocked on this guest only by absent GPU hardware (no render node -> shipped i915/amdgpu call sites unreachable end-to-end); primitive itself fully reproduced.", "evidence": "run.log/run.2.log (baseline counters: stageA_uaf 4/5 then 5/5, stageB_uaf 3/3, canary 3), panic.txt (Fatal trap 12, rip=0xffffffff52170000), build.log (df_sync_witness = single retq), run.fixed.log (post-fix: all 0, guest up under rip_panic triggers), fix.diff", "kernel_refs": [ "sys/dev/drm/include/linux/rcupdate.h:94", "sys/dev/drm/include/linux/rcupdate.h:46", "sys/dev/drm/include/linux/preempt.h:37", "sys/dev/drm/linux_rcu.c:71", "sys/dev/drm/linux_rcu.c:187", "sys/dev/drm/i915/i915_gem.c:3713", "sys/dev/drm/i915/i915_gem.c:3731", "sys/cpu/x86_64/include/cpufunc.h:219" ], "poc_changes": "Rewrote the seeded concept: (1) out-of-tree kmod (SYSDIR=/usr/src/sys, drm include path added AFTER kmod.mk include because kmod.mk resets CFLAGS); (2) fixed DECLARE_MODULE misuse (moduledata_t struct, not handler fn -- the misuse itself panics module_registers strcmp); (3) pinned writer=cpu0 reader=cpu2 and replaced spin handshakes with tsleep-poll (same-CPU spin starved the reader via LWKT cooperative scheduling); (4) removed kprintf from CS hot paths; (5) 8/32-block poison reclaim sprays; (6) sysctl triggers (more/moreb/rip_panic) for reruns and the wild-fp run.", "attempts": 3, "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": 3780, "guest_dirty": 1, "build_cmd": "cd /tmp/poc && make (SYSDIR=/usr/src/sys; CFLAGS+=-I/usr/src/sys/dev/drm/include)", "run_cmd": "kldload drm; kldload /tmp/poc/rcu_race.ko; sleep 45; sysctl rcu_race.stageA_uaf rcu_race.stageB_uaf rcu_race.canary; [RIP run] sysctl rcu_race.rip_panic=1; sysctl rcu_race.moreb=3", "code_hash": "729966d3d8f7922ec134319c18228cfab6c3fc8e2fe4eb548d9f5d596a3c37ba", "notes": "Two harness iterations failed before succeeding (DECLARE_MODULE misuse panic; same-CPU starvation) -- both diagnosed and documented in VERDICT.md. Stage B deterministically hits 3/3 every run; stage A detection 4-5/5. Stock drm.ko loads headless (no GPU needed) and exports call_rcu/__kfree_rcu, so the SHIPPED delay line was exercised, not a copy. Guest was reset with-src after verification.", "recommended_fix": "Implement a real synchronize_rcu() in linux_rcu.c (wait >=2 delay periods AND until the new read-side nesting counter is 0), count read-side critical sections in rcu_read_lock/unlock, and defer rcu_timer reclamation while readers are in flight -- see fix.diff.", "fix_status": "fixed", "fix_baseline_reproduced": true, "fix_patched_reproduced": false, "fix_verdict": "fix.diff applied to guest /usr/src; drm.ko rebuilt (nm: synchronize_rcu/rcu_read_acquire/rcu_read_release present; df_sync_witness now calls synchronize_rcu instead of bare ret); identical harness rebuilt against fixed headers: stageA_uaf 0 (10 rounds), stageB_uaf 0 (6 rounds), canary 0, guest survived rip_panic=1 triggers. Previously-observed UAF/RIP behavior GONE.", "fix_evidence": "run.fixed.log; drmbuild.log; build.log (fixed witness disassembly); fix.diff" } |