⬢ DragonFlyBSD Kernel Audit
DF-5200 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-5200",
  "status": "reproduced",
  "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.",
  "reproduced": true,
  "impact": "panic",
  "confidence": "certain",
  "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": "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)",
  "kernel_refs": [
    "sys/dev/drm/linux_kthread.c:103",
    "sys/dev/drm/linux_kthread.c:110",
    "sys/dev/drm/linux_kthread.c:126",
    "sys/dev/drm/linux_kthread.c:132",
    "sys/dev/drm/scheduler/sched_entity.c:277",
    "sys/dev/drm/scheduler/sched_main.c:514",
    "sys/dev/drm/amd/amdgpu/amdgpu_device.c:3412",
    "sys/dev/drm/amd/amdgpu/amdgpu_debugfs.c:868",
    "sys/kern/lwkt_thread.c:153"
  ],
  "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); <linux/bitops.h> 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-4842s panic).",
  "attempts": 4,
  "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": 30,
  "guest_dirty": 1,
  "build_cmd": "kldload amdgpu; cd /tmp/DF-5200 && make",
  "run_cmd": "kldload /tmp/DF-5200/df5200.ko; sysctl hw.df5200.test=1; sysctl hw.df5200.test=2; sysctl hw.df5200.test=3",
  "code_hash": "07a015c5d0daca8995590f06085bf2415a23c50a5419a942253d33817e97509f",
  "notes": "Root-only KLD on the audit guest (no GPU); on real hardware the racing pair needs no KLD (unprivileged process exit vs ring worker). Distinct from known quartet: DF-2195/4842 = kthread_stop free; DF-2196 = task leak; DF-2197 = park return VALUE; this finding = missing park SYNCHRONIZATION (worker runs after park() returns). The kthread_unpark lwkt_schedule-on-live-thread concern from DF-4842s README was analyzed and killed: tsleep tolerates spurious schedules (kern_synch.c:763-771 _tsleep_remove) and _lwkt_enqueue is TDF_RUNQ-idempotent (lwkt_thread.c:188).",
  "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().",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "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.",
  "fix_evidence": "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))"
}