# DF-3955 — radeon kv_dpm double-free of `rdev->pm.dpm.ps` on the parse-OOM path

**File:** `sys/dev/drm/radeon/kv_dpm.c` (pass 2, GLM 5.3)
**Severity:** Low · **CWE:** CWE-415 (double free) / CWE-401 (leak)
**Confidence:** likely · **Status:** inconclusive on guest (no AMD GPU); the
free/free-again *sequence* replayed in userland (completes silently — DragonFly
userland malloc, like the kernel heap, does not abort).

## What

`kv_parse_power_table()` kv_dpm.c:2673-2677:

```c
ps = kzalloc(sizeof(struct kv_ps), GFP_KERNEL);
if (ps == NULL) {
        kfree(rdev->pm.dpm.ps);     /* freed, pointer NOT NULLed */
        return -ENOMEM;             /* num_ps (set only at :2700) stays 0;
                                       ps_priv of states 0..i-1 LEAK */
}
```

`kv_dpm_init()` propagates the error (:2787-2789) — but `radeon_pm_init_dpm()`
radeon_pm.c:1448 **discards** it, so the device lives on with a dangling
`rdev->pm.dpm.ps` until teardown:

```c
kv_dpm_fini() kv_dpm.c:2861-2871:
  for (i = 0; i < rdev->pm.dpm.num_ps; i++)   /* num_ps == 0: ps_priv leak */
          kfree(rdev->pm.dpm.ps[i].ps_priv);
  kfree(rdev->pm.dpm.ps);                     /* SECOND free => heap corruption */
```

Kernel impact: double free of a `sizeof(struct radeon_ps) × ucNumEntries`
kmalloc block — silent free-list corruption, potentially adjacent to other
live radeon objects. Trigger: `kzalloc(sizeof(struct kv_ps))` (small) failing
mid-parse at boot under memory pressure on KV hardware — rare by nature, hence
Low. amdgpu twin: DF-3891 (Low).

## Reproduce

```
cd findings/poc/DF-3955
./build.sh          # cc -O2 -Wall -o trigger_df3955 trigger_df3955.c
./run.sh
```

Expected: analysis mode stops before the second free (reports dangling pointer
+ leaked count); crash mode performs the identical free/free-again sequence
with the OOM injected at state #2 — note it completes **silently** (no libc
abort on DFly), mirroring the kernel heap's non-defensive behavior.

## Fix

NULL the pointer, free already-parsed `ps_priv`s, and count them (fix.diff).
