# DF-3973 — btc_dpm_init failure ignored: dangling rdev->pm.dpm.ps double-free in btc_dpm_fini (+ r600_free_extended_power_table double free, ps_priv/eg_pi leaks)

- File: `sys/dev/drm/radeon/btc_dpm.c` (pass 2), cross-file sinks rv770_dpm.c / r600_dpm.c / radeon_pm.c
- Class: CWE-416 use-after-free / double free (Medium)
- Family: DF-3645 (ci) / DF-3755 (ni) / DF-3955 (kv) — the btc twin, checklist item (b).

## Build / Run / Expected

Hardware-gated (Barts/Turks/Caicos). Trigger requires a memory-allocation failure at a specific
kzalloc during VBIOS power-table parse, achievable under kernel heap pressure
(spray kmalloc-64/128 until allocation fails at the right site). No PoC without the card.

## Root cause (source-level)

1. `rv7xx_parse_power_table()` — `sys/dev/drm/radeon/rv770_dpm.c:2289-2329`:
   - allocates `rdev->pm.dpm.ps` (2289);
   - on per-state `ps_priv` kzalloc failure (2306-2310) it does
     `kfree(rdev->pm.dpm.ps); return -ENOMEM;` **without NULLing the pointer** and leaks all
     previously allocated `ps_priv` objects. `num_ps` is only set at 2327, so it stays 0 while
     `dpm.ps` dangles.
2. `btc_dpm_init()` — `sys/dev/drm/radeon/btc_dpm.c:2574-2576` — propagates the error.
3. `radeon_pm_init_dpm()` — `sys/dev/drm/radeon/radeon_pm.c:1448` — **ignores** `radeon_dpm_init()`'s
   return and continues to `radeon_dpm_setup_asic`/`radeon_dpm_enable`. boot_ps may already point at
   the freed array (if a BOOT state was parsed before the failing state) → UAF read during enable.
4. On driver teardown, `radeon_pm_fini_dpm()` → `radeon_dpm_fini()` (radeon_pm.c:1705, unconditional)
   → `btc_dpm_fini()` — `btc_dpm.c:2721-2724`:
   - `for (i = 0; i < num_ps; i++) kfree(ps[i].ps_priv);` (0 iterations, num_ps==0)
   - `kfree(rdev->pm.dpm.ps);` — **double free** of the array freed at rv770_dpm.c:2308. If the slab
     object was reused in between, this frees a live foreign object.
5. Second error path: `btc_dpm_init` at 2583-2586, when the `vddc_dependency_on_dispclk.entries`
   kzalloc fails, calls `r600_free_extended_power_table(rdev)` and returns; `r600_free_extended_power_table`
   (r600_dpm.c:1299-1315) kfrees 13 `dyn_state` pointers **without NULLing any of them**. Teardown then
   calls it again from `btc_dpm_fini` (2727) → **double free of every allocated extended-power-table
   object**. `eg_pi` also leaks on every early error return (2559 sets priv; no free on 2570-2586).

## Expected manifestation

Heap corruption (double free) at driver unload/detach, or UAF reads during the ignored-error enable
run. Same class and reachability as DF-3755 (rated Medium, C:H/I:H/A:H).
