{
  "finding_id": "DF-4525",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "amdgpu_free_extended_power_table() (amdgpu_dpm.c:775-792) kfrees all 13 dyn_state power-table pointers without NULLing them; amdgpu_parse_extended_power_table() invokes it on each of its 12 internal error paths (amdgpu_dpm.c:393..727) and returns the error, after which the ASIC teardown layers free the same dangling pointers again (ci_dpm.c:5877 + ci_dpm.c:6321 = triple free on CI; si_dpm.c:7729 and kv_dpm.c:3035 = double free). The defect (dangling pointers + multi-call teardown) was reproduced on the guest against the real stock /boot/kernel/amdgpu.ko: the exact driver call sequence panics the kernel inside amdgpu_free_extended_power_table (memory chunk ... is already free!), and a rebuilt amdgpu.ko carrying fix.diff (NULL-after-free) runs the identical sequence cleanly. In-driver trigger is ENOMEM-gated (all parse error returns are allocation failures; kzalloc(0) returns ZERO_LENGTH_PTR on DragonFly, kern_slaballoc.c:888, so crafted-VBIOS zero counts do not fire it) and requires a CI/SI/KV ASIC, which the guest lacks - reachability is proven statically; no unpriv->root chain claimed.",
  "exploit_chain": "kernel memory pressure during amdgpu attach on a Sea Islands (ci)/Southern Islands (si)/Kaveri (kv) GPU -> kzalloc failure mid power-table parse -> internal free-all leaves dangling pointers -> caller *_dpm_fini frees them again (double/triple kfree) -> slab freelist corruption (panic on allocator-checked builds such as this guest; silent double-alloc primitive on unchecked builds, the theoretical privesc ceiling - not practical here because the trigger cannot be timed deterministically by an unprivileged user)",
  "evidence": "panic.txt + run.log: after 1st free: vddc=0xfffff8008976ba00 (dangling, not NULLed) then panic: memory chunk 0xfffff8008976ba00 is already free! with amdgpu_free_extended_power_table in the stack; fix_run.log: after 1st free: vddc=0 vddci=0, second call no-op, 4 distinct re-allocs, no panic, guest up; fix_build.log shows the patched amdgpu.ko link line; objdump of patched module shows movq $0x0 after each kfree",
  "kernel_refs": [
    "sys/dev/drm/amd/amdgpu/amdgpu_dpm.c:775",
    "sys/dev/drm/amd/amdgpu/amdgpu_dpm.c:393",
    "sys/dev/drm/amd/amdgpu/amdgpu_dpm.c:404",
    "sys/dev/drm/amd/amdgpu/amdgpu_dpm.c:415",
    "sys/dev/drm/amd/amdgpu/amdgpu_dpm.c:426",
    "sys/dev/drm/amd/amdgpu/amdgpu_dpm.c:460",
    "sys/dev/drm/amd/amdgpu/amdgpu_dpm.c:504",
    "sys/dev/drm/amd/amdgpu/amdgpu_dpm.c:559",
    "sys/dev/drm/amd/amdgpu/amdgpu_dpm.c:614",
    "sys/dev/drm/amd/amdgpu/amdgpu_dpm.c:646",
    "sys/dev/drm/amd/amdgpu/amdgpu_dpm.c:669",
    "sys/dev/drm/amd/amdgpu/amdgpu_dpm.c:704",
    "sys/dev/drm/amd/amdgpu/amdgpu_dpm.c:727",
    "sys/dev/drm/amd/amdgpu/amdgpu_dpm.c:765",
    "sys/dev/drm/amd/amdgpu/ci_dpm.c:5777",
    "sys/dev/drm/amd/amdgpu/ci_dpm.c:5877",
    "sys/dev/drm/amd/amdgpu/ci_dpm.c:6321",
    "sys/dev/drm/amd/amdgpu/si_dpm.c:7352",
    "sys/dev/drm/amd/amdgpu/si_dpm.c:7729",
    "sys/dev/drm/amd/amdgpu/kv_dpm.c:3035",
    "sys/kern/kern_slaballoc.c:888",
    "sys/kern/kern_slaballoc.c:947",
    "sys/kern/kern_slaballoc.c:1158"
  ],
  "poc_changes": "PoC authored fresh (no seed): KLD built with the same include set as the shipped amdgpu.ko so struct amdgpu_device (58656 bytes) layout matches; calls the REAL exported amdgpu_free_extended_power_table from stock amdgpu.ko twice on a fabricated adev; fixed two build/load issues (missing display include paths; MODULE_DEPEND version mismatch on amdgpu -> removed, stock amdgpu.ko kldloaded first so the linker resolves the symbol)",
  "attempts": 3,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026\troot@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64",
  "runtime_sec": 1080,
  "guest_dirty": 0,
  "build_cmd": "make -C /root/df4525 SYSDIR=/usr/src/sys KERNBINDIR=/usr/obj/usr/src/sys/X86_64_GENERIC",
  "run_cmd": "kldload amdgpu && kldload /root/df4525/df4525demo.ko   (baseline panics; fix_validate.sh reruns against patched amdgpu.ko)",
  "code_hash": "c0bb8eefb0315c11097456d264950dc1ce99a8dd91658dd406860ab3baed46e4",
  "notes": "Guest has no AMD GPU: in-driver trigger exercised statically (path:line chain), function-level defect + fix reproduced against the real shipped binary. Guest panicked during baseline (db> wedge) and /usr/src was patched for fix validation -> vm.sh reset with-src performed before returning; guest now clean. amdgpu.ko rebuild from patched source takes ~2 min on the guest.",
  "recommended_fix": "NULL all 13 dyn_state pointers in amdgpu_free_extended_power_table() after kfree() (kfree(NULL) is a no-op), making every error-path/fini double-call safe; optionally also make the vddgfx error path (amdgpu_dpm.c:765-768) call amdgpu_free_extended_power_table() like its siblings",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Rebuilt amdgpu.ko from patched /usr/src (fix.diff = NULL-after-free for all 13 pointers; verified movq $0x0 after each kfree in the shipped-symbol disassembly). Identical demo: baseline stock module panics in amdgpu_free_extended_power_table; patched module leaves vddc/vddci NULL after the first free, the second call is a kfree(NULL) no-op, 4 fresh allocations are all distinct, no panic, guest stays up. The double-free behavior is gone.",
  "fix_evidence": "fix_run.log: DF-4525: after 1st free: vddc=0 vddci=0 + MODULES LOADED, NO PANIC; fix_build.log: full patched amdgpu.ko build (DONE_RC=0); baseline A/B in run.log/panic.txt",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 (amdgpu.ko rebuilt from guest /usr/src + fix.diff)"
}