# DF-3587 — Wild OOB kernel read in `btc_find_voltage()` when voltage table count == 0

**File:** `sys/dev/drm/amd/amdgpu/si_dpm.c`
**Lines:** 3206-3215 (sink), reached from 3312-3334 (`btc_apply_voltage_delta_rules`)
and 3626-3631 (`si_apply_state_adjust_rules`)
**Severity:** Medium (crafted-VBIOS kernel panic / wild read)
**Verification:** source-line proof + arithmetic replica harness on the guest
(kernel path itself HW-gated: no AMD GPU on the verification guest).

## What this is

```c
static u16 btc_find_voltage(struct atom_voltage_table *table, u16 voltage)  /* si_dpm.c:3206 */
{
	unsigned int i;
	for (i = 0; i < table->count; i++)
		if (voltage <= table->entries[i].value)
			return table->entries[i].value;
	return table->entries[table->count - 1].value;   /* :3214 -- count==0 -> 0xffffffff */
}
```

`struct atom_voltage_table.count` is a `u32`; with `count == 0` the index
`table->count - 1` wraps to `0xFFFFFFFF`, so the kernel evaluates
`&entries[0] + 4294967295 * 8` — a **~31 GB wild offset** — and reads a `u16`
from it: guaranteed unmapped address => page fault => panic.

## How count can be 0 (all BIOS-controlled)

* `eg_pi->vddci_voltage_table` is only filled when `vddci_control` (GPIO LUT
  object present, `si_dpm.c:4446-4456`) or `vddci_control_svi2` (`:4457-4463`).
  With neither VBIOS object, `count` stays 0 while `si_construct_voltage_tables()`
  still succeeds (VDDC control alone is sufficient) and DPM enables normally.
* Even the filled VDDC table can have `count == 0`: `amdgpu_atombios_get_voltage_table`
  (`amdgpu_atombios.c:1539-1551`) only rejects `ucGpioEntryNum > 32` — a GPIO LUT
  object advertising **0 entries** yields `count = 0` and success.
  (`si_construct_voltage_tables` checks `count == 0` only for **mvdd**
  at `si_dpm.c:4474-4477` — not for vddc/vddci.)

## Trigger path (after dpm enable, on every power-state recompute)

```
amdgpu_pm_compute_clocks
 -> si_dpm_pre_set_power_state            (si_dpm.c:6935)
  -> si_apply_state_adjust_rules          (si_dpm.c:3425)
   -> btc_apply_voltage_delta_rules       (si_dpm.c:3626-3631)
    -> btc_find_voltage(&eg_pi->vddci_voltage_table|vddc_voltage_table, ...)  (:3324/:3330)
```
with BIOS power-state `usVDDC`/`usVDDCI` values such that
`(vddc - vddci) > vddc_vddci_delta` (default 200, `si_dpm.c:7447`) and neither
voltage is 0 (`si_dpm.c:3319` guard).

## How to reproduce (arithmetic replica)

```
cc -O2 -Wall -o /tmp/df3587_harness /tmp/df3587_harness.c   # on the DragonFly guest
/tmp/df3587_harness
```
Expected: index `4294967295`, byte offset `0x7fffffff8`, child SIGSEGV on the
wild read (`run.log`, deterministic over 2 runs).

## Fix

`fix.diff` — return 0 when `count == 0` (voltage-delta rule then degrades to the
`max_vddc/max_vddci` clamp already present in `btc_apply_voltage_delta_rules`).
