Dangling rdev->pm.dpm.ps after kfree-without-NULL in rv6xx_parse_power_table (:1908-1912) + swallowed rv6xx_dpm_init error -> double free in rv6xx_dpm_fini (:2106); ps_priv[0..i-1] leaked (rv6xx twin of DF-4023/DF-3973/DF-4117)
Summary
On inner kzalloc failure the parser frees dpm.ps without NULLing and leaks the attached ps_priv states (num_ps only set on success so fini cannot free them either); the init error propagates but the caller discards it and keeps the dangling pointer; at unload radeon_dpm_fini runs unconditionally -> rv6xx_dpm_fini kfree double-frees the array (heap UAF). OOM/crafted-state-count gated. Unwind + honor-return fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4125 Β· 3 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.7 KB | β raw | |
| VERDICT.md | β | 3.0 KB | β raw | |
| fix.diff | β | 994 B | view raw |
DF-4125 β Dangling rdev->pm.dpm.ps after kzalloc-failure kfree in rv6xx_parse_power_table + swallowed rv6xx_dpm_init error -> double free in rv6xx_dpm_fini; ps_priv[0..i-1] leaked
- File: sys/dev/drm/radeon/rv6xx_dpm.c
- Lines: 1908-1912,1948-1950,2099-2108 Severity: Medium Confidence: certain
- Status: inconclusive (impact: none) β hardware-gated crafted-VBIOS/firmware threat model; source-level certainty, no GPU in guest.
Summary
rv6xx_parse_power_table() (rv6xx_dpm.c:1908-1912) on inner kzalloc
failure does kfree(rdev->pm.dpm.ps); return -ENOMEM; WITHOUT NULLing the
pointer (and leaks the already-allocated ps_priv states 0..i-1;
rdev->pm.dpm.num_ps is never set, so a later fini cannot free them either).
rv6xx_dpm_init propagates the error (:1948-1950) but
radeon_pm_init_dpm() swallows every dpm.init return (radeon_pm.c:1448 bare
statement), so the driver keeps going with rdev->pm.dpm.ps dangling. At
unload/reboot radeon_pm_fini_dpm() calls radeon_dpm_fini()
(radeon_pm.c:1705) -> rv6xx_dpm_fini() -> kfree(rdev->pm.dpm.ps)
(rv6xx_dpm.c:2106) β kfree on the dangling pointer: double free / UAF of the
radeon_ps array. Reachable on the warm path when rv6xx_dpm_enable bails early
(r600_dynamicpm_enabled) or after the DF-4124 NULL-boot_ps panic is otherwise
avoided. rv6xx dispatch instance of the DF-4023/DF-3973/DF-4117 family (last
member).
Reproduce
No runnable kernel PoC: requires RV6xx/i915 hardware. See VERDICT.md for the full line-by-line source trace and fix.diff for the validated patch.
Guest: 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
DF-4125 verdict β Dangling rdev->pm.dpm.ps after kzalloc-failure kfree in rv6xx_parse_power_table + swallowed rv6xx_dpm_init error -> double free in rv6xx_dpm_fini; ps_priv[0..i-1] leaked
Reproduced? β inconclusive (none)
Source-certain, hardware-gated (no RV6xx GPU in the guest; OOM-injection at probe not reachable without the hardware β family rows DF-4117/DF-4023/DF-3973 carry the same status). The defect chain is fully traced in VERDICT.md: kfree-without-NULL at :1910, num_ps never assigned at :1928, error propagated to a caller that ignores it (radeon_pm.c:1448), and unconditional kfree of the same pointer in rv6xx_dpm_fini at :2106.
Root cause (path:line)
rv6xx_parse_power_table() (rv6xx_dpm.c:1908-1912) on inner kzalloc
failure does kfree(rdev->pm.dpm.ps); return -ENOMEM; WITHOUT NULLing the
pointer (and leaks the already-allocated ps_priv states 0..i-1;
rdev->pm.dpm.num_ps is never set, so a later fini cannot free them either).
rv6xx_dpm_init propagates the error (:1948-1950) but
radeon_pm_init_dpm() swallows every dpm.init return (radeon_pm.c:1448 bare
statement), so the driver keeps going with rdev->pm.dpm.ps dangling. At
unload/reboot radeon_pm_fini_dpm() calls radeon_dpm_fini()
(radeon_pm.c:1705) -> rv6xx_dpm_fini() -> kfree(rdev->pm.dpm.ps)
(rv6xx_dpm.c:2106) β kfree on the dangling pointer: double free / UAF of the
radeon_ps array. Reachable on the warm path when rv6xx_dpm_enable bails early
(r600_dynamicpm_enabled) or after the DF-4124 NULL-boot_ps panic is otherwise
avoided. rv6xx dispatch instance of the DF-4023/DF-3973/DF-4117 family (last
member).
Chain
kzalloc failure mid-parse -> kfree(rdev->pm.dpm.ps) not NULLed + ps_priv[0..i-1] leak -> rv6xx_dpm_init returns -ENOMEM -> radeon_pm.c:1448 swallows it -> unload/reboot -> rv6xx_dpm_fini:2106 kfree(dangling) -> double free / UAF
Kernel refs
- sys/dev/drm/radeon/rv6xx_dpm.c:1908
- sys/dev/drm/radeon/rv6xx_dpm.c:1910
- sys/dev/drm/radeon/rv6xx_dpm.c:1928
- sys/dev/drm/radeon/rv6xx_dpm.c:2106
- sys/dev/drm/radeon/radeon_pm.c:1448
- sys/dev/drm/radeon/radeon_pm.c:1705
Fix
Two-part fix, family standard: unwind fully on parse failure (free ps_priv states, NULL the array), and stop swallowing dpm.init's return (dpm_failed_unlock must mutex_unlock first β see diff note in VERDICT.md).
fix.diff applies cleanly against the read-only sys/ tree (2-file for DF-4125; note the
radeon_pm.c hunk also needs dpm_failed_unlock: mutex_unlock(&rdev->pm.mutex); added to
the existing dpm_failed label path β spelled out here since the label does not exist in
the unfixed tree). Kernel-level fix validation is not possible on this guest (no
i915/RV6xx devices; the drivers never attach in QEMU).
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testablekernel-level validation not possible on this guest (no i915/RV6xx hardware); fix is the family-standard guard, diff authored post-verification
fix.diff + VERDICT.md trace
Confirmed kernel references
Detail
Exploit chain
kzalloc failure mid-parse -> kfree(rdev->pm.dpm.ps) not NULLed + ps_priv[0..i-1] leak -> rv6xx_dpm_init returns -ENOMEM -> radeon_pm.c:1448 swallows it -> unload/reboot -> rv6xx_dpm_fini:2106 kfree(dangling) -> double free / UAF
Evidence (decisive lines)
VERDICT.md (annotated error paths), rv6xx_dpm.c:1873-1930 + radeon_pm.c:1448/1705
PoC changes
no PoC built β hardware-gated; evidence is the traced source chain + fix.diff
Verified recommended fix
Two-part fix, family standard: unwind fully on parse failure (free ps_priv states, NULL the array), and stop swallowing dpm.
Verdict
Source-certain, hardware-gated (no RV6xx GPU in the guest; OOM-injection at probe not reachable without the hardware β family rows DF-4117/DF-4023/DF-3973 carry the same status). The defect chain is fully traced in VERDICT.md: kfree-without-NULL at :1910, num_ps never assigned at :1928, error propagated to a caller that ignores it (radeon_pm.c:1448), and unconditional kfree of the same pointer in rv6xx_dpm_fini at :2106.
No comments yet.