β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-5200

kthread_park() provides no quiescence: returns without waiting for the target to park while kthread_parkme() never deschedules - the park/unpark contract drm_sched_entity_fini() and amdgpu GPU-reset rely on is void, giving a deterministic use-after-free on entity teardown

Summary

kthread_park returns immediately (no wait-for-park handshake) and kthread_parkme's deschedule is a NO-OP on a running thread - every consumer races the worker it believes stopped: drm_sched_entity_fini parks-then-frees while the worker is mid-select/pop on that entity (unpriv process-exit path on GPU systems; also every amdgpu GPU reset). Guest-verified 5/5: worker executed ~25 entity touches after park returned; entity_fini-shaped park;free;kzalloc produced a silent write-UAF into the same-address victim (10 writes/round, 82 UAF reads) + controlled panic proof. Park-handshake fix validated on rebuilt drm.ko (0/5 violations).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5200 Β· 13 files
FileTypeDescriptionSize
df5200.c β€” 10.3 KB view raw
Makefile β€” 328 B ↓ download
build.sh β€” 94 B view raw
run.sh β€” 186 B view raw
build.log β€” 1.3 KB view raw
run.log β€” 1.5 KB view raw
run.2.log β€” 1.3 KB view raw
panic.txt β€” 5.6 KB view raw
run_fixed.log β€” 2.0 KB view raw
fix.diff β€” 2.2 KB view raw
env.txt β€” 431 B view raw
manifest.json β€” 1.2 KB view raw
verdict.json β€” 4.6 KB view raw

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

fix.diff applied to guest /usr/src, drm.ko rebuilt from patched linux_kthread.c, module re-run: baseline 5/5 RAN WHILE PARKED + 5/5 UAF corruptions + 82 UAF reads; patched 0/5 violations (visits frozen 13->13 all rounds), 0 corruptions, 0 UAF reads, guest stable.

run_fixed.log (t1: '13 -> 13 (frozen (correct))' x5, '0/5 rounds violated'; t2: '0/5 rounds corrupted the reallocated victim ... 0 UAF reads of freed memory (badmagic 0 -> 0)')
↓ fix.diffper-fix-DF-5200

Confirmed kernel references

Detail

Exploit chain

unprivileged GPU client races process-exit/entity-fini (park; kfree(entity/jobs); unpark) against the ring scheduler worker that the park was supposed to quiesce -> worker UAF-writes freed drm_sched_entity/job objects (victim contents attacker-influenced via GPU submissions). Demonstrated on guest to the silent write-UAF-into-reallocated-object stage + panic proof; full uid=0 chain not constructed (no GPU on audit guest: no drm victim grooming available).

Evidence (decisive lines)

run.log (t1: 5/5 RAN WHILE PARKED), run.2.log (t2: 5/5 'UAF WRITE PROVEN ... overlap=yes', visits=10, payload[30]=0x4141414141414141, 82 badmagic reads), panic.txt (panic: DF5200 ... trace df5200_test2 <- sysctl), run_fixed.log (patched: 0/5, frozen 13->13, 0 corruptions, 0 UAF reads)

PoC changes

no seed PoC existed; authored fresh. Fixes during bring-up: sysctl node is hw.df5200.test (not hw.df5200_test); ASCII-only comments (kernel -Werror); include; victim re-arm moved before unpark so the patched kernel scores a clean 0 (fair fix-validation); teardown avoids kthread_stop() (that is DF-4842's panic).

Verified recommended fix

Implement the park handshake: kthread_parkme() publishes KTHREAD_IS_PARKED and sleeps on its task_struct until unparked; kthread_park() waits (bounded) for that bit; kthread_unpark() drops the raw lwkt_schedule() and just clears the flag + wake_up_process().

Verdict

REPRODUCED on the QEMU guest via a KLD driving the pristine in-tree shim functions (kthread_run/kthread_should_park/kthread_parkme/kthread_park/kthread_unpark from drm.ko) with a drm_sched_main()-shaped worker. kthread_park() returns immediately and kthread_parkme()'s lwkt_deschedule_self() is a no-op on a running thread (TDF_RUNQ clear), so the worker keeps running: 5/5 rounds showed ~25 entity touches during a 500ms window that begins when kthread_park() returned (Linux contract: zero), and the drm_sched_entity_fini()-shaped sequence park(); kfree(entity); kzalloc(victim) yielded a deterministic silent write-UAF into the reallocated victim at the SAME address in 5/5 rounds (worker incremented victim->visits 10x/round, wrote 0x4141414141414141 payload slots, 82 UAF reads of freed memory), plus a controlled panic proof. Root cause: sys/dev/drm/linux_kthread.c:103-110 (no wait-for-park) + :126-133 (parkme no-op); consumers relying on the contract: sched_entity.c:273-279 (entity teardown), amdgpu_device.c:3412/3441 (GPU reset ring quiesce), amdgpu_debugfs.c:868/884.