# DF-3585 — Heap OOB write in `si_get_svi2_voltage_table()` + `si_populate_smc_voltage_table()` (SVI2 voltage tables)

**File:** `sys/dev/drm/amd/amdgpu/si_dpm.c`
**Lines:** 4410-4414 (primary sink), 4505-4506 (secondary sink), reached via 4436-4441 / 4457-4463
**Severity:** High (kernel heap memory corruption, crafted-VBIOS gated)
**Verification:** logic-replica harness on the DragonFly guest (guest has no AMD GPU —
hardware trigger not testable); source-level certainty.

## What this is

`si_construct_voltage_tables()` builds the VDDC/VDDCI voltage tables either from a
GPIO LUT atom object (bounded: `amdgpu_atombios.c:1539` rejects `ucGpioEntryNum > 32`)
**or**, on SVI2 platforms, by copying a clock/voltage *dependency table* whose `count`
is a **raw BIOS u8 with no clamp**:

* `si_dpm.c:4410` `voltage_table->count = voltage_dependency_table->count;`
* `si_dpm.c:4411-4414` loop writes `entries[i]` for `i < count`

`voltage_dependency_table->count` comes from
`amdgpu_parse_clk_voltage_dep_table()` (`amdgpu_dpm.c:285,302`) =
`ucNumEntries` straight from the VBIOS PowerPlayInfo
`VddcDependencyOnMCLK` / `VddciDependencyOnMCLK` tables (0..255).
The destination `struct atom_voltage_table.entries[]` is a **fixed 32-entry array**
(`MAX_VOLTAGE_ENTRIES`, `amdgpu_atombios.h:119-133`) embedded in the single
`kzalloc`'d `struct si_power_info` (`si_dpm.c:7311`).

=> up to **(255-32) * 8 = 1784 bytes of heap OOB write** with BIOS-chosen 16-bit
values at 8-byte strides (each slot: `value` u16 + 2 stale pad bytes + `smio_low`=0).

* First OOB slot (`entries[32]`) lands exactly on `eg_pi->vddci_voltage_table.count`
  -> corrupted with a **BIOS-chosen u16**, cascading into further OOB in every later
  consumer (`si_populate_voltage_value`, `btc_find_voltage`, ...).
* Overflow smashes `bootup_arb_registers`, `ulv`, `ats`, `current_rps.ps_priv` /
  `requested_rps.ps_priv` (kernel pointer slots) and everything after, inside the
  `si_power_info` allocation.

Second sink, same root cause: `si_populate_smc_voltage_table()` (`si_dpm.c:4505-4506`)
does `table->lowSMIO[i] |= entries[i].smio_low` for `i < count`; `lowSMIO[]` is fixed
`SISLANDS_MAX_NO_VREG_STEPS = 32` (`sislands_smc.h:207,215`) inside
`SISLANDS_SMC_STATETABLE smc_statetable` — with an SVI2 VDDCI table (mixed
GPIO-VDDC + SVI2-VDDCI config, all BIOS-advertised) the loop runs to `count` = up to
255, doing an OOB read-modify-write across the statetable and (for i >= ~154) **past
the end of the `si_power_info` heap allocation**.

## Reachability

Crafted VBIOS on an SI (Tahiti/Pitcairn/Verde/Oland/Hainan) card:
1. no `VOLTAGE_OBJ_GPIO_LUT` VDDC object + a `VOLTAGE_OBJ_SVID2` VDDC object
   (`si_dpm_init`, si_dpm.c:7390-7399) -> `si_pi->voltage_control_svi2 = true`;
2. `VddcDependencyOnMCLK` table with `ucNumEntries` > 32 (any u8);
3. on `si_dpm_enable()` (boot / module load / resume) `si_construct_voltage_tables()`
   (si_dpm.c:6792-6798 -> 4436-4441) fires the overflow **before** any other
   power-state validation.
Inherited verbatim from the Linux amdgpu sync (KMS 3.27-era, Linux ~4.5/4.6); the
same unclamped loops exist upstream.

## How to reproduce (logic replica — guest has no AMD GPU)

```
# on the DragonFly guest (files transferred to /tmp):
cc -O2 -Wall -o /tmp/df3585_harness /tmp/df3585_harness.c
/tmp/df3585_harness
```

Expected (see `run.log`, deterministic across runs):
* Part A: `OOB WRITE CONFIRMED` — 446 non-zero bytes rewritten past `entries[32]`,
  `vddci_voltage_table.count` becomes 4128 (`0x1020`, the BIOS u16 we planted at
  dependency entry 32).
* Part B: child `SIGSEGV` — the same stores leave the object's mapping.
* Part C: child `SIGSEGV` at `lowSMIO[32]` — second sink is OOB.
* Part D: with the `fix.diff` clamp — table rejected, no corruption.

## Artifacts

* `harness.c` — replica with verbatim struct defs (amdgpu_atombios.h:119-133,
  sislands_smc.h:207-224) and verbatim loops (si_dpm.c:4407-4414, 4503-4506)
* `build.sh`, `run.sh`
* `build.log`, `run.log`, `run.2.log` — full untrimmed guest output
* `env.txt` — guest uname, PCI inventory (virtio only, no AMD GPU), amdgpu.ko
  present + loads (no si_dpm attach), dmesg showing harness SIGSEGV children
* `fix.diff` — git-apply-able fix (clamp both construction and population)
* `VERDICT.md`, `manifest.json`, `verdict.json`
