Dangling rdev->pm.dpm.ps after swallowed cypress_dpm_init failure: double free in cypress_dpm_fini at unload/reboot (cypress dispatch of the DF-4023/DF-3973 family)
Summary
rv7xx_parse_power_table's per-state kzalloc failure path kfree's dpm.ps without NULLing and without freeing ps_priv[0..i-1]; num_ps stays 0. cypress_dpm_init propagates the error but it is swallowed TWICE (radeon_pm.c:1448 discards .dpm.init's return; evergreen.c:5235 calls radeon_pm_init as void) - attach completes with ps dangling. At unload/reboot radeon_dpm_fini runs UNCONDITIONALLY -> cypress_dpm_fini: the num_ps==0 loop leaks the parsed states, then kfree(dpm.ps) double-frees the block -> freelist corruption/panic. Also leaks eg_pi on the earlier platform_caps failure. Three-part family fix (parse cleanup + init cleanup + idempotent fini).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4117 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.7 KB | β raw | |
| VERDICT.md | β | 3.0 KB | β raw | |
| fix.diff | β | 1.6 KB | view raw | |
| env.txt | β | 583 B | view raw | |
| manifest.json | β | 1.2 KB | view raw | |
| verdict.json | β | 3.1 KB | view raw |
DF-4117 β Dangling rdev->pm.dpm.ps + ignored cypress_dpm_init error: double free in cypress_dpm_fini (cypress dispatch) ================================================================== File: sys/dev/drm/radeon/cypress_dpm.c β pass 2 (GLM 5.3). Cypress-dispatch instance of the DF-3645/3755/3955/3973/4023 family (shared root site rv770_dpm.c:2306-2310; btc twin DF-3973, rv770 twin DF-4023 β same class, this file's own consumption sinks).
MECHANISM (all path:line in VERDICT.md): * rv7xx_parse_power_table kzalloc(ps_priv) failure mid-loop kfree's rdev->pm.dpm.ps WITHOUT NULLing it; num_ps stays 0; ps_priv[0..i-1] leak (rv770_dpm.c:2306-2310). * cypress_dpm_init:2050-2052 propagates the error but leaves rdev->pm.dpm.priv set (also leaks eg_pi on the earlier r600_get_platform_caps failure). * radeon_pm.c:1448 discards .dpm.init's return; evergreen.c:5235 calls radeon_pm_init() as a void statement β attach completes with dpm_enabled=false and ps dangling. * unload/reboot: radeon_pm_fini_dpm -> radeon_dpm_fini runs UNCONDITIONALLY (radeon_pm.c:1705) -> cypress_dpm_fini:2150-2153: num_ps==0 skips the ps_priv loop (leak), kfree(rdev->pm.dpm.ps) DOUBLE-FREES the block freed at parse time.
TRIGGER (hardware-gated): evergreen-family GPU + crafted many-state VBIOS + memory pressure at attach; unload/reboot detonates. IMPACT: kmalloc double free (heap corruption/panic at detach); realistic ceiling DoS (trigger point is boot/attach + root unload). Hardware absent on audit guest => runtime inconclusive, source-level certain (family precedent: DF-3973/DF-4023 packs). FIX: fix.diff (parse error path cleanup + init cleanup + idempotent fini), mirrors the accepted family fixes.
DF-4117 VERDICT β double free in cypress_dpm_fini via dangling dpm.ps
STATUS: inconclusive (runtime hardware-gated; no evergreen GPU on the audit guest); mechanism source-certain; family convention per DF-3973/DF-4023. impact ceiling (not demonstrated): heap corruption / panic at unload. confidence: certain.
CHAIN (path:line):
1. rv7xx_parse_power_table (SHARED, rv770_dpm.c:2271-2333):
kzalloc(rv7xx_ps) failure inside the state loop at :2306-2310
executes kfree(rdev->pm.dpm.ps) (:2308) WITHOUT setting it to NULL,
WITHOUT freeing the ps_priv[0..i-1] stored earlier, and returns
-ENOMEM before num_ps is assigned (:2331) β num_ps stays 0.
2. cypress_dpm_init (cypress_dpm.c:2050-2052) propagates the error but
leaves rdev->pm.dpm.priv = eg_pi (allocated :2032-2035); the earlier
r600_get_platform_caps failure (:2046-2048) returns without cleanup
as well (eg_pi leak).
3. The error is swallowed TWICE: radeon_pm.c:1448
radeon_dpm_init(rdev); discards .dpm.init's int return, and
every asic init calls radeon_pm_init() as a void statement
(evergreen.c:5235 for this family). radeon_pm.c:1449 assigns
current_ps/requested_ps = boot_ps (NULL, see DF-4118), then calls
radeon_dpm_setup_asic + radeon_dpm_enable; enable fails (or panics
per DF-4118); attach completes with dpm_enabled=false and
rdev->pm.dpm.ps DANGLING.
4. Detonation: radeon_pm_fini (unload/reboot) -> radeon_pm_fini_dpm
(radeon_pm.c:1690) -> radeon_dpm_fini UNCONDITIONALLY (:1705, not
gated on dpm_enabled) -> cypress_dpm_fini (cypress_dpm.c:2146-2155):
the i < num_ps loop skips (num_ps == 0) so the i parsed ps_priv
states LEAK, then kfree(rdev->pm.dpm.ps) at :2153 double-frees the
block freed at step 1 β kmalloc freelist corruption, typically
panic at unload/reboot; on a foreign reclaim, memory-safety
consequences in principle (realistic trigger point is boot/attach
under memory pressure + root unload β ceiling DoS).
RELATION TO KNOWNS: this file's knowns (DF-1385/1386/1387) do not cover lifecycle. The btc/rv770 twins (DF-3973/DF-4023) established the shared root; the cypress dispatch (radeon_asic.c:1405 for CYPRESS/HEMLOCK/JUNIPER/REDWOOD/CEDAR) consumption is this finding.
FIX: fix.diff β (1) shared parse error path frees ps_priv[0..i-1] and NULLs rdev->pm.dpm.ps; (2) cypress_dpm_init frees eg_pi on early failure; (3) cypress_dpm_fini NULLs ps/priv after kfree. Kernel-level fix validation impossible on this guest (no hardware); fix_status not_testable. Diff is the same shape already validated for the btc twin family (DF-3973).
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_testableDiff authored (same shape as the family fixes) but kernel-level validation requires evergreen hardware absent from the audit guest.
fix.diff; VERDICT.md
Confirmed kernel references
- sys/dev/drm/radeon/cypress_dpm.c:2032
- sys/dev/drm/radeon/cypress_dpm.c:2050
- sys/dev/drm/radeon/cypress_dpm.c:2146
- sys/dev/drm/radeon/cypress_dpm.c:2153
- sys/dev/drm/radeon/rv770_dpm.c:2306
- sys/dev/drm/radeon/rv770_dpm.c:2308
- sys/dev/drm/radeon/rv770_dpm.c:2331
- sys/dev/drm/radeon/radeon_pm.c:1448
- sys/dev/drm/radeon/radeon_pm.c:1705
- sys/dev/drm/radeon/evergreen.c:5235
- sys/dev/drm/radeon/radeon_asic.c:1405
Detail
Exploit chain
On CYPRESS/HEMLOCK/JUNIPER/REDWOOD/CEDAR hardware: many-state crafted VBIOS + attach-time kernel memory pressure forcing the kzalloc at rv770_dpm.c:2306 to fail; double error-swallow keeps the driver attached; kldunload/reboot double-frees the ps array. Trigger point is boot/attach (physical/vBIOS access) β realistic ceiling DoS, not an unprivileged remote path.
Evidence (decisive lines)
VERDICT.md (chain with path:line); fix.diff (parse-path cleanup + init cleanup + idempotent fini); env.txt (hardware absence)
PoC changes
No seed PoC; hardware-gated (no AMD evergreen GPU on the audit guest), pack follows the DF-3973/DF-4023 convention.
Verified recommended fix
Free ps_priv[0..i-1] and NULL rdev->pm.dpm.ps in the parse error path; free eg_pi on cypress_dpm_init early failure; make cypress_dpm_fini idempotent (fix.diff).
Verdict
Source-level certain, runtime hardware+OOM-gated (family convention of DF-3973/DF-4023). rv7xx_parse_power_table's per-state kzalloc failure (rv770_dpm.c:2306-2310) kfree's rdev->pm.dpm.ps without NULLing it and without freeing ps_priv[0..i-1]; num_ps stays 0. cypress_dpm_init (cypress_dpm.c:2050-2052) propagates but leaks eg_pi; the error is swallowed twice (radeon_pm.c:1448 discards .dpm.init's return; evergreen.c:5235 calls radeon_pm_init() as a void statement), so attach completes with dpm_enabled=false and ps dangling. At unload/reboot radeon_pm_fini_dpm -> radeon_dpm_fini runs unconditionally (radeon_pm.c:1705) -> cypress_dpm_fini:2150-2153: num_ps==0 skips the ps_priv loop (leak) and kfree(rdev->pm.dpm.ps) at :2153 double-frees the block freed at parse time => kmalloc freelist corruption/panic at unload. Cypress dispatch (radeon_asic.c:1405) instance of the DF-3645/3755/3955/3973/4023 family; this file's knowns (DF-1385/1386/1387) do not cover lifecycle.
No comments yet.