{
  "finding_id": "DF-4515",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "dma_buf_get() on this port drops its file reference before returning (linux_dma-buf.c:184 dropfp), so drm_gem_prime_fd_to_handle()s trailing dma_buf_put (drm_prime.c:864, also :848/:857) is an unbalanced fdrop and the dma_buf pointer is used unreferenced across the whole ioctl. Proven on the guest against the shipped drm.ko: repeated F2H lookup-hit cycles drive f_count 2->1->0, dma_buf_ops.release fires while the prime fd is still installed (premature free), a subsequent dma_buf_get on the installed fd is a use-after-free, and the process exit panics with fdrop: invalid f_count -1. Guest has no GPU so the unprivileged ioctl entry itself could not be driven; the KLD executes the exact drm_prime.c call sequence through the real module symbols. Fix (keep the holdfp reference) validated on the same guest: f_count stable, no panic.",
  "exploit_chain": "On GPU hardware (source-certain, not exercisable in this guest): video-group user opens /dev/dri/cardN -> DRM_IOCTL_PRIME_HANDLE_TO_FD (export, ~4 file refs: falloc + obj->dma_buf + prime-member + fdtab) -> DRM_IOCTL_PRIME_FD_TO_HANDLE once (import) -> repeat FD_TO_HANDLE on the same fd: each ioctl drops one f_count (drm_prime.c:864) -> after ~4 repeats f_count==0 -> dmabuf_close kfrees the M_DRM dma_buf and drops the GEM object while fd + GEM handle + rb-tree member still point at it -> spray M_DRRM-sized objects -> hijack dmabuf->ops->release (called via dmabuf_close, linux_dma-buf.c:63) or corrupt the freed struct file -> code exec in kernel context -> uid=0. The racing-close variant frees the dma_buf mid-ioctl for a second UAF window. Not carried to uid0 here only because the QEMU guest has no DRM device.",
  "evidence": "run.log (f_count 2->1->0 + release fired ... fd still installed + survived UAF touch), panic.txt (panic: fdrop: invalid f_count -1, trace fdrop<-closef<-fdfree<-exit1<-sys_exit), fix_run.log (f_count stays 2, no panic after fix.diff), VERDICT.md (full narrative)",
  "kernel_refs": [
    "sys/dev/drm/drm_prime.c:802",
    "sys/dev/drm/drm_prime.c:811",
    "sys/dev/drm/drm_prime.c:848",
    "sys/dev/drm/drm_prime.c:857",
    "sys/dev/drm/drm_prime.c:864",
    "sys/dev/drm/linux_dma-buf.c:168",
    "sys/dev/drm/linux_dma-buf.c:184",
    "sys/dev/drm/linux_dma-buf.c:55",
    "sys/dev/drm/drm_gem.c:270",
    "sys/kern/kern_descrip.c:2206"
  ],
  "poc_changes": "Original audit sketch was ioctl-driven; guest has no GPU, so the PoC was rebuilt as a KLD that calls the shipped drm.ko symbols dma_buf_export/dma_buf_fd/dma_buf_get/dma_buf_put in the exact drm_prime.c order. Sysctl-per-step did not work (each sysctl(8) is its own process, so the fd died between steps) - steps folded into one handler (fire=5).",
  "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": 1140,
  "guest_dirty": 0,
  "build_cmd": "cd /usr/src/sys/dev/drm/drm && make obj && make -j4; cd /root/df4515 && make",
  "run_cmd": "kldload /usr/obj/usr/src/sys/dev/drm/drm/drm.ko; kldload /root/df4515/df4515mod.ko; sysctl hw.df4515mod.fire=5",
  "code_hash": "68d0f0556809bb4d163b0f09a510f82bd33e0571d9c299f2e37ba4a088ed5e4a",
  "notes": "The unbalanced-put arithmetic was confirmed against kern_descrip.c: falloc sets f_count=1, fsetfd fholds (+1), dma_buf_get holdfp+dropfp = net 0, dma_buf_put fdrop -1. dma_buf_fds CLOEXEC (#if 0) and the fd-install path are balanced - the ONLY defect is dma_buf_gets early drop. drm_prime.c:802 is the sole dma_buf_get caller in the tree, so the fix has zero blast radius. Guest reset clean after runs.",
  "recommended_fix": "dma_buf_get(): keep the holdfp() reference (drop the early dropfp) so the returned dma_buf carries the fget-style reference drm_prime.c:864s dma_buf_put expects.",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff applied to guest /usr/src, drm.ko + df4515mod rebuilt, identical PoC step 5 re-run: f_count remained 2 through all three F2H cycles (baseline: 2->1->0 + free + panic), the post-free touch operated on a live file, and the guest kept running (baseline panicked in fdrop at process exit). Bad behavior gone; correct error/return semantics intact.",
  "fix_evidence": "findings/poc/DF-4515/fix_run.log vs run.log + panic.txt"
}