# DF-3587 Verification Verdict

**Severity:** Medium — crafted-VBIOS wild kernel read / panic (CWE-125 / CWE-191)
**Verification method:** source-line proof + arithmetic replica on the DragonFly
guest; kernel path HW-gated (no AMD GPU on guest) => `inconclusive` at kernel
level, `certain` at source level.

## Proof

1. Sink: `sys/dev/drm/amd/amdgpu/si_dpm.c:3214`
   `return table->entries[table->count - 1].value;` with `count` a `u32` that is
   **never checked against 0** in `btc_find_voltage()` (si_dpm.c:3206-3215).
   `count == 0` -> index `0xFFFFFFFF` -> address `entries + 0x7FFFFFFF8` (~31 GB).

2. count == 0 is reachable with a crafted (or merely sparse) VBIOS:
   * VDDCI: no GPIO-LUT and no SVID2 VDDCI object -> table never filled
     (`si_construct_voltage_tables`, si_dpm.c:4446-4463 — vddci is optional;
     only VDDC control is mandatory, `:4426-4444`).
   * VDDC: GPIO LUT object with `ucGpioEntryNum == 0` is accepted
     (`amdgpu_atombios.c:1539-1540` only rejects `> 32`, `:1551` sets
     `count = ucGpioEntryNum`). The `count == 0` sanity check exists only for
     the mvdd table (`si_dpm.c:4474-4477`), not for vddc/vddci.

3. Call chain: `si_apply_state_adjust_rules` (si_dpm.c:3626-3631) runs on every
   power-state recompute (`si_dpm_pre_set_power_state`, si_dpm.c:6943) and calls
   `btc_apply_voltage_delta_rules` (si_dpm.c:3312) whenever the BIOS-supplied
   level voltages differ by more than `vddc_vddci_delta` (200 mV default,
   `si_dpm.c:7447`); `:3324` (vddci table) or `:3330` (vddc table) then hits
   the unchecked `entries[count-1]`.

4. Impact: single wild `u16` read from a non-canonical-for-KVA-distance address;
   no data is returned to the attacker (value feeds a voltage clamp). Result:
   kernel page fault -> panic (local DoS at driver-load/resume/state-change time,
   gated on crafted VBIOS contents).

## Harness (guest, DragonFly 6.5-DEVELOPMENT, base gcc)

```
[2] index = (u32)(count - 1) = 4294967295
[2] byte offset = 4294967295 * 8 = 0x7fffffff8 (~31 GB past the table)
[3] child killed by SIGSEGV => kernel-equivalent wild read -> page fault
[4] RESULT: UNCHECKED count==0 -> WILD OOB READ CONFIRMED (kernel: panic)
```
Deterministic across 2 runs (`run.log`, `run.2.log`).

## Why not kernel-level on this guest

No AMD PCI device (`pciconf -l`: virtio only; 0 devices with vendor 0x1002);
`amdgpu.ko` loads but no SI asic attaches, so `si_dpm_*` never executes.

## Fix validation

`fix.diff`: `if (table->count == 0) return 0;` before the `entries[count-1]`
access. Logic-validated (the wrap is prevented; callers already clamp the result
to `max_vddc`/`max_vddci`, `si_dpm.c:3326/3332`). Patch is git-apply-verified
against the read-only `sys/` tree (never applied there). Kernel build not
repeated this run (guest budget); change is a 3-line pure guard.

## Verdict

**inconclusive** at kernel level (HW absent on verification guest);
source-certain wild-read defect; recommended one-line guard as in `fix.diff`.

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