# DF-1136 — VERDICT

**Verdict: CONFIRMED via source-trace + userspace harness. On-guest: INCONCLUSIVE (HW-gated — no AMD GPU; radeon not in GENERIC, only LINT64).**

## Root-cause confirmation
`si_get_svi2_voltage_table()` (`si_dpm.c:3939-3958`) copies the VBIOS-sourced count
straight into the fixed-size table:
`voltage_table->count = voltage_dependency_table->count;` (`si_dpm.c:3951`) then
`for (i=0;i<count;i++){ entries[i].value=...; entries[i].smio_low=0; }`
(`si_dpm.c:3952-3955`). `struct atom_voltage_table.entries` is a fixed
`entries[MAX_VOLTAGE_ENTRIES]` with `MAX_VOLTAGE_ENTRIES = 32` (`radeon_mode.h:671,684`).
`count` originates in the VBIOS power tables (atom `ucNumEntries`, u8 0..255) and is
stored in a `u32` field (`radeon.h:1399-1401`), so it is effectively unbounded from
the kernel's perspective. Any `count > 32` writes past `entries[32]`.

The defect is asymmetric in its callers: the **GPIO** voltage path trims via
`si_trim_voltage_table_to_fit_state_table(SISLANDS_MAX_NO_VREG_STEPS)`
(`si_dpm.c:3973-3976` vddc, `:3993-3996` vddci), but the **two SVI2 call sites**
(`si_dpm.c:3977-3982` vddc, `:3998-4004` vddci) call `si_get_svi2_voltage_table()`
with **no trim**. `voltage_table` is `eg_pi->vddc_voltage_table` / `vddci_voltage_table`,
embedded in `struct evergreen_power_info`, so `entries[32+]` overwrite the following
struct fields (cac_weights, powertune pointers) — corrupted state dereferenced during
`si_dpm_enable`.

## Evidence
- `harness.c` (run as unprivileged `maxx`) shows VBIOS counts 33/48/64/255 produce
  1/16/32/223 OOB writes past `entries[32]`; a concrete `count=48` model writes 128
  bytes of an adjacent canary (128 = (48-32) * sizeof(atom_voltage_table_entry)).

## Exploit chain / impact
This is a **write** primitive (CWE-787). On GENERIC it is not compiled in (radeon
absent). On real Southern Islands hardware with a malicious VBIOS, the writes corrupt
adjacent `evergreen_power_info` fields including pointers used by the DPM enable path
-> realistically a **panic** (corrupted-pointer deref) on INVARIANTS-ON GENERIC; on a
non-INVARIANTS build the corrupted pointers could be leveraged (no SMEP/SMAP) toward
code-exec. No `uid=0` chain developed — path unreachable on this guest. Realistic
ceiling: **panic** / corruption.

## Fix validation
`fix.diff` clamps `voltage_table->count` to `MAX_VOLTAGE_ENTRIES` (32) before the loop
(matching the GPIO path's trim intent).
- `git apply --check -p1` => OK.
- Compiles in the radeon module: targeted `make si_dpm.o` built `si_dpm.o` (86880 B,
  `-Werror` clean); full `nativekernel` rc=0.
- Harness "WITH FIX" pass: **0/6** counts produce any OOB.
- `fix_status: not_testable` (HW-gated runtime; apply-check + compile + harness fix-demo + trace all pass).

## PoC changes
Evidence pack authored from scratch: `harness.c` (+ WITH-FIX pass), `build.sh`,
`run.sh`, `fix.diff`, `VERDICT.md`, `manifest.json`, logs.

## Kernel refs (confirmed during verification)
`sys/dev/drm/radeon/si_dpm.c:3951`, `:3952`, `:3977`, `:3998`;
`sys/dev/drm/radeon/radeon_mode.h:671`, `:684`; `sys/dev/drm/radeon/radeon.h:1399`.
