DF-3611 / verdict.json
{ "finding_id": "DF-3611", "status": "reproduced", "reproduced": 1, "impact": "dos", "confidence": "certain", "verdict": "radeon twin of amdgpu DF-3588. si_parse_power_table kfrees rdev->pm.dpm.ps at :6855 when a ni_ps kzalloc fails, but leaves the pointer dangling, num_ps==0 (so the earlier ps_priv allocations leak) and returns -ENOMEM. radeon_pm_init_dpm:1448 ignores the init return; on driver unload si.c:6945 -> radeon_pm_fini:1705 calls radeon_dpm_fini UNCONDITIONALLY (no dpm_enabled check) -> si_dpm_fini:7085 kfrees the dangling pointer: double free of a block that the heap has meanwhile recycled -> live-object free / cross-object UAF. Harness replicates the sequence with fault injection: the freed dpm.ps block is recycled by a live victim object; finis second free releases it; the next allocation aliases it and writes through the second owner clobber the first owners data (observed marker corruption). OOM-gated (needs the ni_ps kzalloc to fail while later allocs succeed), same trigger class and Low severity as the amdgpu twin.", "exploit_chain": "kzalloc failure at state i -> kfree(dpm.ps), pointer dangling, privs leaked -> init rc ignored -> heap recycles block -> driver unload -> radeon_dpm_fini unconditional -> kfree(dpm.ps) again -> live object freed -> UAF/type confusion for any same-size-class object", "evidence": "run.log [A]: victim object now lives at 0x8004904a0 (SAME block - recycled) ... >>> DOUBLE FREE of a LIVE object <<< ... victim->marker was silently CLOBBERED -> use-after-free; [B]: si_dpm_fini: dpm.ps == NULL -> no-op (no double free)", "kernel_refs": [ "sys/dev/drm/radeon/si_dpm.c:6855", "sys/dev/drm/radeon/si_dpm.c:7085", "sys/dev/drm/radeon/radeon_pm.c:1448", "sys/dev/drm/radeon/radeon_pm.c:1705", "sys/dev/drm/radeon/si.c:6945" ], "poc_changes": "n/a (pass-2 finding authored with its harness; no seeded PoC to fix)", "attempts": 2, "guest_uname": "DragonFly dfbsd 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": 90, "guest_dirty": 0, "build_cmd": "cd findings/poc/DF-3611 && sh build.sh", "run_cmd": "cd findings/poc/DF-3611 && sh run.sh", "code_hash": "a2ccba867562301dfabdfa8ed5d78ed1105571bb96430f80ffd1011138fe7ab6", "notes": "Harness-level reproduction (exact kernel expressions executed with crafted-VBIOS inputs; SIGSEGV/SIGFPE/corruption observed). No AMD SI GPU on the virtio-only QEMU guest -> no live radeon DPM path possible; radeon.ko present at /boot/kernel. Guest /usr/src restored to pristine (sha 774b9b851b3b4ccb...) after fix validation.", "recommended_fix": "see fix.diff", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "git apply --check OK (standalone and stacked with the other four si_dpm.c fixes); targeted module TU compile `make KERNBUILDDIR=/usr/obj/usr/src/sys/X86_64_GENERIC si_dpm.o` => rc=0, -Werror clean (87080 B). Harness fixed-path shows the primitive gone (fix.diff frees the already-parsed ps_priv states, NULLs rdev->pm.dpm.ps on the error path, and guards si_dpm_fini with `if (rdev->pm.dpm.ps)`. git apply --check OK; stacked module TU compile -Werror clean; harness [B] clean.)", "fix_evidence": "fix.diff frees the already-parsed ps_priv states, NULLs rdev->pm.dpm.ps on the error path, and guards si_dpm_fini with `if (rdev->pm.dpm.ps)`. git apply --check OK; stacked module TU compile -Werror clean; harness [B] clean." } |