β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4125

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4125 Β· 3 files
FileTypeDescriptionSize
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

VERDICT.md
↓ download raw

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

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_testable
baseline no→ patch + rebuild →patched clean

kernel-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
↓ fix.diffDragonFly 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

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.