{
  "finding_id": "DF-4820",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "dos",
  "confidence": "likely",
  "verdict": "radeon_ring_restore() returns early at radeon_ring.c:350-352 when radeon_ring_lock() fails, leaking the kvmallocd ring-backup buffer; the only caller (radeon_gpu_reset, radeon_device.c:1881-1882) ignores the return value and never frees it on the reset-succeeded branch. During the restore loop needs_reset is still true (cleared later at radeon_device.c:1921-1922), so the fence wait inside radeon_ring_alloc returns -EDEADLK (radeon_fence.c:516-517) or -ENOENT (radeon_fence.c:649-654), making the failure deterministic whenever the ring was saturated (backup size within align_mask of ring_size/4 aligns up to exactly ring_size/4, unsatisfiable on the fresh post-resume ring). Verified on the DragonFly guest by compiling the VERBATIM kernel function body inside a mock environment reproducing these kernel-proven conditions: 1000/1000 simulated reset cycles leak their 64 KiB buffer in both error modes (full untrimmed run.log), 0/1000 with fix.diff applied. Full end-to-end trigger (CS flood + GPU deadlock + lockup reset) requires physical radeon hardware, absent from the QEMU guest; impact ceiling is repeatable kernel heap exhaustion (local DoS, kernleak bucket), not an info leak and not a privesc primitive.",
  "exploit_chain": "unpriv /dev/dri user: (1) sustain CS submissions to keep ring near-saturated with in-flight dwords; (2) deadlock GPU (user-requested cross-ring semaphores in CS) -> lockup detector (radeon_lockup_timeout, ~10s); (3) radeon_gpu_reset: backup saves ~ring_size/4-1 dwords (kvmalloc ring_size bytes); (4) hw reset + resume succeed (fresh empty ring); (5) restores alloc aligns to ring_size/4 -> fence wait -> -EDEADLK (needs_reset still set) -> early return without kvfree; caller ignores rc. Repeat per cycle: leak ~ring_size bytes / ~10 s / ring -> kernel heap exhaustion DoS.",
  "evidence": "run.log (baseline: LIVE (leaked) blocks: 1000 (65532000 bytes = 63996 KiB) x2 modes, then FIXED: LIVE ... 0 ... fix VALIDATED x2); run.2.log (500-iter stability); build.log (verbatim body extraction + compile); VERDICT.md (kernel-condition derivation with path:line); fix.diff",
  "kernel_refs": [
    "sys/dev/drm/radeon/radeon_ring.c:341",
    "sys/dev/drm/radeon/radeon_ring.c:350",
    "sys/dev/drm/radeon/radeon_ring.c:317",
    "sys/dev/drm/radeon/radeon_device.c:1881",
    "sys/dev/drm/radeon/radeon_device.c:1921",
    "sys/dev/drm/radeon/radeon_fence.c:516",
    "sys/dev/drm/radeon/radeon_fence.c:649",
    "sys/dev/drm/radeon/cik.c:4104"
  ],
  "poc_changes": "n/a \u2014 finding and PoC authored in this run; harness build script extracts the function body verbatim from the kernel tree rather than transcribing it",
  "attempts": 3,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly v6.5.0.1712.g89e6a-DEVELOPMENT #1: Mon Jun 29 14:18:01 UTC 2026 x86_64",
  "runtime_sec": 10,
  "guest_dirty": 0,
  "build_cmd": "./build.sh && ./build.sh fixed",
  "run_cmd": "./run.sh",
  "code_hash": "f1725d5703935eafa43276f5a4f5258b47d88abc3c743013d9a9dd9f762739a4",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff adds kvfree(data) before the error return in radeon_ring_restore. Same verbatim body, one-line change: baseline leaks 1000/1000 blocks per error mode (63996 KiB), patched leaks 0/1000 with identical error returns \u2014 leak eliminated, behavior otherwise unchanged. Kernel-rebuild validation not performed: the guest has no radeon GPU and no kernel tree on this snapshot; the harness compiles the verbatim kernel body itself, so the fix is exercised against the exact shipped code.",
  "fix_evidence": "run.log FIXED sections: allocations freed: 1000 ... LIVE (leaked) blocks: 0 ... RESULT: FIXED build leaks nothing \u2014 fix VALIDATED (both -EDEADLK and -ENOENT modes); fix.diff",
  "notes": "Cross-ref for maintainers: radeon_device.c:1885 kfree()s the kvmallocd buffer on the failed-reset branch instead of kvfree() (latent invalid-free only under vmalloc fallback) \u2014 different file, already audited pass 1. Bug byte-identical in Linux v4.15 and v6.1 (never fixed upstream).",
  "recommended_fix": "Free the backup buffer on the error path: brace the `if (r)` in radeon_ring_restore() and kvfree(data) before returning (see fix.diff)."
}