rcu_read_lock() is cpu_ccfence() (no exclusion) and reclaim is a fixed 1s delay line β __kfree_rcu/call_rcu free objects while readers are still inside their critical sections (deterministic 3/3 UAF + RIP control, demonstrated)
Summary
The read side is a compiler fence (readers may preempt/block in-section) and reclaim is a fixed 1s timer with no reader coupling - any read-side section outliving 1s is handed freed memory. Demonstrated deterministically 3/3 with the real drm.ko __kfree_rcu ring: reader blocked 2s in-section, object freed at +1s, slot reclaimed with controlled content, waking reader called the planted function pointer; RIP-control trap at attacker RIP. 7 shipped call sites in the drm tree. Same protocol fix validated (3/3 -> 0/6).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5126 Β· 16 files| File | Type | Description | Size | |
|---|---|---|---|---|
| rcu_race.c | β | 11.3 KB | view raw | |
| Makefile | β | 217 B | β download | |
| build.sh | β | 89 B | view raw | |
| run.sh | β | 191 B | view raw | |
| run_fixed.sh | β | 523 B | view raw | |
| build.log | β | 5.6 KB | view raw | |
| run.log | β | 966 B | view raw | |
| run.2.log | β | 547 B | view raw | |
| run.fixed.log | β | 1.3 KB | view raw | |
| panic.txt | β | 1.1 KB | view raw | |
| env.txt | β | 514 B | view raw | |
| dmesg.txt | β | 7.3 KB | view raw | |
| drmbuild.log | β | 25.0 KB | view raw | |
| fix.diff | β | 2.5 KB | view raw | |
| VERDICT.md | β | 4.0 KB | β raw | |
| verdict.json | β | 3.9 KB | view raw |
DF-5126 VERDICT β no-op read side + fixed 1s delay line
Status: REPRODUCED (deterministic 3/3 per run; RIP control; fix validated)
Root cause
sys/dev/drm/include/linux/rcupdate.h:46-56:rcu_read_lock()/rcu_read_unlock()map topreempt_disable()/preempt_enable().sys/dev/drm/include/linux/preempt.h:37-38: those map tocpu_ccfence()β a compiler reordering fence only (sys/cpu/x86_64/include/cpufunc.h:219-227). No crit section, no preemption exclusion, no assertion preventing a thread from blocking inside the section.sys/dev/drm/linux_rcu.c:79-105,161-184: reclamation is a per-CPU ring whose entries fire afterdelta >= hzβ a fixed one-second timer, with no coupling to readers whatsoever.
Therefore any read-side critical section that survives longer than the delay
(stalled, preempted, or blocked β all permitted by the shim) has its object
freed underneath it by the shipped __kfree_rcu()/call_rcu() path.
Runtime proof (stock /boot/kernel/drm.ko, 6-CPU guest)
Stage B of the harness (writer cpu0 / reader cpu2; __kfree_rcu is the REAL
drm.ko symbol; victim allocated/freed with the shim's own kzalloc/kfree
(M_DRM) so slab zones match):
- reader:
rcu_read_lock(); v = rcu_dereference(gp); tsleep(2s); read v->magic; rcu_read_unlock(); - writer:
rcu_assign_pointer(gp, NULL); __kfree_rcu(v1); tsleep(1.4s); [32-object poison reclaim spray] - The shim's timer (
drm: expand RCU cpu 0 to 16in dmesg β the real ring) frees v1 at ~+1s β well inside its own grace model β while the reader is provably still inside its critical section. - Reader wakes at +2s, still in-section: reads MAGIC_POISON and calls
v->v_fpβ a function pointer that exists only as data written into the freed-and-reclaimed object.
Results: 3/3 rounds, every baseline run (runs 1, 2, 3 and the extra trigger
rounds). With rip_panic=1 the reclaimed fp = 0xffffffff52170000 and the
kernel took Fatal trap 12: page fault ... instruction pointer = 0xffffffff52170000
(panic.txt) β arbitrary kernel RIP control from the UAF.
Distinguishing from neighbors
- DF-5033 (amdgpu_bo_list.c): lockless
idr_find()vs synchronous kfree inidr_grow()β a different subsystem and an immediate free; that finding cites linux_rcu.c:187 only as context. This finding is about the RCU shim's own grace-period model (delay line + no-op read side) and is demonstrated through the shim's exported__kfree_rcu. - DF-1881 (linux_fence.c/reservation): a specific unsafe refcount helper; not the shim's grace model.
Exploit chain / ceiling
Identical to DF-5125's (same object, same reclaim discipline): blocked/preempted
reader -> __kfree_rcu fires at +1s -> reclaim with controlled content ->
reader consumes poisoned fields -> indirect call through reclaimed fp ->
RIP control (fatal trap shown). Real-world ceiling: every drm kfree_rcu/call_rcu
victim reachable from unprivileged GPU clients (fences, bo lists, gem objects,
reservation arrays). This guest has no GPU/render node, so the shipped call
sites cannot be driven end-to-end here β environment blocker, documented; the
primitive (UAF + RIP control via the shipped shim) is fully reproduced.
Fix validation
fix.diff (shared with DF-5125): rcu_read_lock()/rcu_read_unlock() now
maintain a global nesting counter (rcu_read_acquire/rcu_read_release);
rcu_timer() defers element execution while rcu_readers != 0; the blocked
reader therefore holds off the free until after its unlock β exactly the
semantics RCU promises. Post-fix stage B: 0 hits in 6 rounds (including
rip_panic=1 trigger rounds), guest healthy. Baseline was 3/3 every run.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
fixedfix.diff applied in guest /usr/src, drm.ko rebuilt, identical harness rebuilt against fixed headers: stageB_uaf 0/6 rounds (was 3/3 per run), canary 0, guest survived rip_panic=1 trigger rounds. Delay-line free now correctly deferred until the blocked reader exits its section.
run.fixed.log; drmbuild.log; fix.diff
Confirmed kernel references
- sys/dev/drm/linux_rcu.c:81
- sys/dev/drm/linux_rcu.c:71
- sys/dev/drm/linux_rcu.c:187
- sys/dev/drm/linux_rcu.c:127
- sys/dev/drm/include/linux/rcupdate.h:46
- sys/dev/drm/include/linux/rcupdate.h:52
- sys/dev/drm/include/linux/preempt.h:37
- sys/dev/drm/scheduler/sched_fence.c:112
- sys/dev/drm/amd/amdgpu/amdgpu_fence.c:648
- sys/dev/drm/linux_reservation.c:196
Detail
Exploit chain
reader cpu2 blocks 2s INSIDE rcu_read_lock (legal under this shim: preempt_disable is cpu_ccfence) holding v -> writer cpu0 swaps gp and calls the shipped __kfree_rcu(v) -> shim's per-cpu ring (dmesg: 'drm: expand RCU cpu 0 to 16') kfrees v at ~+1s per its fixed delta>=hz rule -> same-cpu slab reclaim with controlled {magic,fp} -> reader wakes still in-section, reads poison (UAF read) and calls v->v_fp (kernel indirect call via freed-object content) -> fp=0xffffffff52170000: Fatal trap 12, RIP = attacker value. uid=0 end-to-end blocked on this guest only by absent GPU hardware (no render node); primitive fully reproduced.
Evidence (decisive lines)
run.log/run.2.log (stageB_uaf 3/3, poison_reads 3, canary 3 every run), panic.txt (rip=0xffffffff52170000), run.fixed.log (post-fix stageB_uaf 0 across 6 rounds incl rip_panic=1, guest up), fix.diff
PoC changes
Shared harness with DF-5125 (stage B is this finding's trigger); all harness repairs listed in DF-5125/verdict.json poc_changes apply. Deterministic by construction: reader's 2s in-section sleep spans the shim's 1s grace line.
Verified recommended fix
Account read-side critical sections (rcu_read_acquire/release in rcu_read_lock/unlock) and defer rcu_timer reclamation while any reader is in flight -- see fix.diff.
Verdict
The drm RCU shim's read side (rcu_read_lock -> preempt_disable -> cpu_ccfence) provides no preemption/blocking exclusion while its reclaim side is a fixed 1-second delay line with no reader tracking (linux_rcu.c:81-83), so __kfree_rcu/call_rcu free objects while readers are still inside their critical sections. Reproduced deterministically (3/3 every run): a reader blocked 2s inside rcu_read_lock() had its object freed at ~+1s by the REAL drm.ko __kfree_rcu ring, the slot reclaimed with controlled content, and then read poisoned data and CALLED a function pointer written into the freed object; with rip_panic=1 the kernel took a fatal trap at the attacker-chosen RIP (0xffffffff52170000). fix.diff (read-side nesting counter + deferral of rcu_timer reclamation while readers are in flight) drives stage-B hits to 0.
No comments yet.