# DF-4188 — VERDICT

**Status: REPRODUCED (logic-level harness; kernel-level repro hardware-gated)**
**Impact: kernel heap double free (freelist corruption → panic / potential UAF)**
**Confidence: certain**

## Root cause

`sumo_parse_power_table()` (`sys/dev/drm/radeon/sumo_dpm.c:1455-1528`) frees
the power-state array on an allocation failure **without clearing the pointer**:

```c
ps = kzalloc(sizeof(struct sumo_ps), GFP_KERNEL);        /* :1500 */
if (ps == NULL) {
        kfree(rdev->pm.dpm.ps);                           /* :1502 — dangling! */
        return -ENOMEM;                                   /* :1503 */
}
```

`rdev->pm.dpm.ps` now points at freed memory. `sumo_dpm_init()` propagates the
error (`:1783-1785`), but `radeon_pm.c:1448` **discards** it (the DF-4162 hub
bug), so the driver loads with:

- `rdev->pm.dpm.ps` → freed block (dangling),
- `rdev->pm.dpm.num_ps` still 0 (only set on success, `:1526`),
- `ps_priv[0..i-1]` leaked.

On unload/reboot, `radeon_pm_fini()` (`radeon_pm.c:1721-1727`, still
`PM_METHOD_DPM` — the method is never reset on the swallowed failure) →
`radeon_pm_fini_dpm()` (`:1705`) → `sumo_dpm_fini()`:

```c
for (i = 0; i < rdev->pm.dpm.num_ps; i++)                 /* :1876 — 0 iters */
        kfree(rdev->pm.dpm.ps[i].ps_priv);
kfree(rdev->pm.dpm.ps);                                   /* :1879 — DOUBLE FREE */
```

A second kfree of an already-freed kmalloc block corrupts the kernel heap
freelist. The intervening `radeon_dpm_enable()` normally panics first through
the DF-4187 NULL-boot_ps path, *except* when the hardware already reports
`DPM_SCLK_ENABLE` (warm reset): then `sumo_dpm_enable()` early-returns -EINVAL
at `:1209-1210`, no crash, and the machine runs until unload/reboot — exactly
the DF-4117 (cypress) scenario "double free at unload/reboot".

Secondary leak on the same error ladder: the `-EINVAL` return at `:1498-1499`
leaves `dpm.ps` and all `ps_priv` states allocated (no double free there, just
leaks); the ENOMEM path additionally leaks `ps_priv[0..i-1]`.

## Trigger / threat model

kzalloc failure for a ~128-byte `struct sumo_ps` at exactly state N ≥ 1 while
states 0..N-1 already parsed — i.e., transient kernel memory exhaustion during
radeon driver load on a Sumo/Ontario/Palm APU (memory pressure, coredumps,
fork bombs racing module load). No VBIOS crafting required (any table with ≥ 2
states), though a crafted multi-state table widens the window. Combined with
the DF-4162 swallow: driver keeps running; reboot/kldunload → heap corruption
→ panic or worse (double free of a 96/128-byte block is a classic exploitable
primitive if the block got reallocated in between — reallocation is likely
given the driver continues to allocate).

## Harness proof (mode 4188)

Verbatim replica of `sumo_parse_power_table` + `sumo_dpm_fini`; canary
allocator replaces the kernel heap; the Nth kzalloc is forced to fail exactly
like GFP pressure would:

```
[sumo_ps#1] kzalloc(#6, 128) -> INJECTED FAILURE (ENOMEM)
sumo_parse_power_table() = -12
rdev->pm.dpm.ps is NOT NULL after the failed parse     <- dangling pointer kept
sumo_dpm_fini() ...
*** CANARY ALLOCATOR: DOUBLE FREE of 0x8004d1d00 (size 96) DETECTED ***
exit=98
```

The kernel performs the identical two kfrees on the identical pointer
(`sumo_dpm.c:1502` then `:1879`); only the allocator differs.

## Fix validation

1. `harness_fixed 4188` (`-DFIX_DF4188`: free `ps_priv[i-1..0]`, kfree the
   array, NULL it): "rdev->pm.dpm.ps is NULL after the failed parse", fini
   completes, `canary_violations = 0`, exit 0.
2. `fix.diff` applied with the other three sumo fixes on the guest `/usr/src`
   (`fix_combined.diff`, see `radeon_fix_apply.log` hunk 3): full `radeon.ko`
   module build under `-Werror`, `MAKE_RC=0` (`radeon_module_build.log`).

Kernel-on-hardware validation not possible on the audit guest (no ATI APU).

## Kernel references

- sys/dev/drm/radeon/sumo_dpm.c:1500-1504 (kfree-without-NULL)
- sys/dev/drm/radeon/sumo_dpm.c:1876-1879 (fini double free)
- sys/dev/drm/radeon/sumo_dpm.c:1783-1785 (init propagates)
- sys/dev/drm/radeon/radeon_pm.c:1448 (DF-4162 swallow)
- sys/dev/drm/radeon/radeon_pm.c:1705, :1721-1727 (fini_dpm reachable)
- sys/dev/drm/radeon/sumo_dpm.c:1209-1210 (enable early-return that lets the
  machine survive to unload — warm-reset scenario)

## 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.
