{
  "finding_id": "DF-5169",
  "status": "reproduced",
  "reproduced": true,
  "impact": "kernel heap UAF write (memory corruption): 8-byte NULL write at freed-object +16 (work->worker) and 1-byte zero at +25 (work->running) into attacker-reclaimable M_DRM-class memory, plus wakeup() on a dangling wait channel; deterministic primitive proof without panic; reliable uid=0 chain not developed (trigger needs pre-LLC Intel GPU, not emulatable in QEMU)",
  "confidence": "certain",
  "verdict": "i915_clflush_work() legally self-frees its container inside the work callback (last dma_fence_put at i915_gem_clflush.c:89, with refs #2/#3 already dropped via FENCE_FREE at sw-fence completion and excl-fence replacement by the next execbuf), but the DFly LinuxKPI process_all_work() (linux_workqueue.c:92-98) reads and writes the work item after func() returns: work->on_queue read, work->worker=NULL 8-byte NULL write, work->canceled read, work->running=false 1-byte zero write, wakeup(work) on the dangling address. Proven deterministically on the guest with a KLD that reproduces the exact self-free choreography on the real system_wq (16/16 baseline rounds show both UAF writes landing in freed-then-reclaimed-and-poisoned memory, same-address reclaim 16/16); fix.diff (worker-owned completion state, no post-func work touches) rebuilt on the guest eliminates the writes (0/8 + 3 stability reruns) while normal workqueue operation is preserved.",
  "exploit_chain": "unpriv GPU user: execbuf#1 on cache-dirty non-LLC i915 object -> async clflush queued (fence refs: creator+sw+resv) -> sw-fence completion fires FENCE_COMPLETE(schedule_work) then FENCE_FREE(dma_fence_put) -> execbuf#2 write on same object replaces excl fence (reservation_object_add_excl_fence -> dma_fence_put of clflush fence) -> worker lag lets both drops land before the callback runs -> i915_clflush_works final dma_fence_put is the last ref -> kfree(clflush) INSIDE callback -> process_all_work post-func block writes NULL qword (+16) and zero byte (+25) into the freed chunk and calls wakeup() on its address -> same-size M_DRM realloc (demonstrated same-address 16/16) places a victim object under the writes: nulled pointer -> NULL-deref DoS, zeroed count/flag byte -> premature-free/state-confusion follow-on UAF. PoC proves the workqueue half deterministically on QEMU (no i915 GPU emulatable); full uid=0 chain not developed.",
  "evidence": "run.log + run.dmesg.log + run.2.log: running byte=00 (poison=41) worker qword=0000000000000000 ... <== UAF WRITE running=false <== UAF WRITE worker=NULL x8 rounds x2 runs; run.fix.log: running byte=41 ... worker qword=4141414141414141 ... -> post-func writes did NOT touch the freed item (fixed behavior) x8; run.fix.stability.log: 3 reruns 0 writes + clean kldunload/kldload; VERDICT.md section 2 for the i915-side ref choreography with path:line",
  "kernel_refs": [
    "sys/dev/drm/i915/i915_gem_clflush.c:89",
    "sys/dev/drm/i915/i915_gem_clflush.c:104",
    "sys/dev/drm/i915/i915_gem_clflush.c:148-166",
    "sys/dev/drm/linux_workqueue.c:92-98",
    "sys/dev/drm/linux_workqueue.c:87-89",
    "sys/dev/drm/i915/i915_sw_fence.c:187-195",
    "sys/dev/drm/i915/i915_vma.c:926-934",
    "sys/dev/drm/linux_reservation.c:243-244",
    "sys/dev/drm/linux_reservation.c:260"
  ],
  "poc_changes": "Seed sketch had no runnable form; wrote selffree.c from scratch as a KLD mirroring i915_clflush_works self-free pattern on the real system_wq with same-CPU same-size reclaim+poison (0x41, on_queue kept 0 so the worker takes the worker=NULL branch), plus a FIFO marker work for completion detection that works identically on patched/unpatched kernels; Makefile reuses the DF-5128 drm-header KLD harness",
  "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 (baseline) / #1: Wed Sep 30 16:10:55 UTC 2026 (fix validation)",
  "runtime_sec": 1020,
  "guest_dirty": 1,
  "build_cmd": "cd /root/selffree && make   ; fix phase: cd /usr/src && git apply /root/pack/fix.diff && make -j6 nativekernel KERNCONF=X86_64_GENERIC && make installkernel KERNCONF=X86_64_GENERIC",
  "run_cmd": "kldload drm; kldload /root/selffree/selffree.ko; sysctl debug.selffree=1; dmesg | grep SELFFREE | tail -13",
  "code_hash": "8c8c5b8229ae33420a399e78eb05d828c931dcfc6aa6d1f0a1f4e5a32777507c",
  "notes": "Guest reset (with-src) performed after validation so the next spawn starts clean. The i915-side trigger is code-proven but HW-gated (no Intel GPU in QEMU): this is the only in-tree self-freeing work item found (all container_of(work,...) callbacks under sys/dev/drm audited); the workqueue-side violation binds for ANY future self-freeing work ported into the LinuxKPI. Adjacent pre-existing workqueue defects (DF-1976/1977/1978/1979/1980/4918/4919) are NOT re-reported; this finding is the touch-after-free specifically.",
  "recommended_fix": "process_all_work() must not touch *work after func() returns: keep completion bookkeeping in worker-owned state (worker->running_work), snapshot the cancel flag pre-func, and make work_busy/flush_work/flush_workqueue/_cancel_work consult the workers running_work under its lock (see fix.diff)",
  "fix_status": "fixed",
  "fix_baseline_reproduced": true,
  "fix_patched_reproduced": false,
  "fix_verdict": "fix.diff applied inside guest /usr/src (git apply clean), kernel+drm.ko rebuilt (nativekernel, 35,472-line log) and installed; the exact same PoC then showed poison fully intact 8/8 rounds (no post-func writes into freed memory), marker work still executed every round, 3 stability reruns + kldunload/kldload clean; baseline (pre-fix) had shown the writes 16/16 rounds",
  "fix_evidence": "run.fix.log, run.fix.stability.log (post-fix: VERDICT FIXED ... did NOT touch the freed item), buildkernel.fix.log, installkernel.log, dmesg.fix.txt (kern.version #1 Sep 30 16:10:55), vs run.log/run.2.log (baseline UAF writes)"
}