# DF-3611 — Double free of rdev->pm.dpm.ps (and leak of parsed ps_priv states) on the allocation-failure path of si_parse_power_table

**Severity:** Low  **Confidence:** certain  **Status:** reproduced (harness)
**Impact:** dos

## What

si_parse_power_table kfrees rdev->pm.dpm.ps at :6855 when a ni_ps kzalloc fails, but leaves the pointer dangling, num_ps==0 (so the earlier ps_priv allocations leak) and returns -ENOMEM. radeon_pm_init_dpm:1448 ignores the init return; on driver unload si.c:6945 -> radeon_pm_fini:1705 calls radeon_dpm_fini UNCONDITIONALLY (no dpm_enabled check) -> si_dpm_fini:7085 kfrees the dangling pointer: double free of a block that the heap has meanwhile recycled -> live-object free / cross-object UAF. Harness replicates the sequence with fault injection: the freed dpm.ps block is recycled by a live victim object; fini's second free releases it; the next allocation aliases it and writes through the second owner clobber the first owner's data (observed marker corruption). OOM-gated (needs the ni_ps kzalloc to fail while later allocs succeed), same trigger class and Low severity as the amdgpu twin.

## Reproduce

```
sh build.sh   # cc -O2 -Wall -o harness harness.c
sh run.sh     # prints [A] buggy-path behaviour, [B] fixed-path behaviour
```

Expected decisive output (see run.log): the `[A]` section shows the fault or
corruption the kernel would take; the `[B]` section shows the behaviour with
fix.diff applied.

No AMD GPU is attached to the audit QEMU guest (virtio-only), so the radeon
DPM path cannot be exercised live; the harness replicates the exact kernel
expressions with crafted-VBIOS inputs (house methodology, cf. DF-1137..1140).

## Fix

`fix.diff` — validated: `git apply --check` clean; `make si_dpm.o` in
sys/dev/drm/radeon with KERNBUILDDIR=/usr/obj/usr/src/sys/X86_64_GENERIC
compiles -Werror clean with all five pass-2 fixes stacked; harness `[B]`
shows the primitive gone.
