# DF-4492 — rs780_dpm.c: unchecked VBIOS pplib offsets/indices → wild OOB kernel reads

## What

`rs780_parse_power_table()` (sys/dev/drm/radeon/rs780_dpm.c:789-844) walks the
VBIOS PowerPlayInfo table using **only attacker-supplied fields** — none are
bounds-checked against the table or the BIOS image:

* state array walk `:812-816`: `bios + data_offset + usStateArrayOffset + i*ucStateEntrySize`
  (u16 offset + up to 254×255 stride ⇒ up to ~64 KB past the table start)
* non-clock info `:817-821`: `usNonClockInfoArrayOffset + ucNonClockStateIndex*ucNonClockSize`
  (u8 index × u8 stride ⇒ up to 65025 bytes)
* clock info `:822-827`: `usClockInfoArrayOffset + ucClockStateIndices[0]*ucClockInfoSize`
  (up to 65025 bytes)

`atom_parse_data_header()` (atom.c:1366) validates nothing beyond a non-zero
master-table pointer, and the radeon BIOS reader (radeon_bios.c:98)
`kzalloc`s exactly the ROM image size — so the reads run past the kmalloc'd
BIOS buffer into adjacent kernel heap. The ingested bytes land in
`rps->class/class2/caps/vclk/dclk` and `ps->sclk_low/sclk_high/min_voltage`
and are (a) printed to the console / debugfs via
`rs780_dpm_print_power_state()` (`:944-948`) and (b) programmed into PM
registers.

Sumo twin of DF-4190, trinity twin of DF-4146.

## Threat model

Crafted/malicious VBIOS image on an RS780/RS880 IGP (flashed ROM, replaced
card, or a passed-through GPU in a VM). Far indices fault the kernel
(page-fault panic = DoS); near indices silently ingest adjacent kernel-heap
bytes into user-visible state (info leak class).

## Reproduce (on the DragonFly audit guest, or any cc)

```
./build.sh          # cc -O2 -fno-delete-null-pointer-checks, baseline + -DFIX_ALL
./run.sh            # modes 4492 (redzone poison ingestion) and 4492g (guard page)
```

Expected baseline (see run.log):

* mode 4492: three `*** CANARY ... heap OOB access ***` reports and
  `rps[0].class = 0xa5a5 (== canary poison: OOB READ CONFIRMED)`,
  `caps/vclk = 0xa5a5a5a5`, exit=98
* mode 4492g: `*** SIGNAL 11 at address 0x8004c4280 ***` — SIGSEGV exactly
  in the PROT_NONE guard page behind the 16 KB BIOS image replica, exit=99

Expected with `harness_fixed`: both modes print
`DRM_ERROR: ... walks past the table` and exit=0.

Kernel-level reproduction is hardware-gated (no RS780-class GPU exists in
the QEMU guest); the harness is a verbatim replica of the parse code —
see VERDICT.md.

## Fix

`fix.diff` (also in this directory): request the table size from
`atom_parse_data_header()` and bound every walk by it; reject
`ucStateEntrySize < 2`. Validated: applies to the guest's pristine
`/usr/src`, `radeon.ko` builds under `-Werror` (radeon_module_build.log,
MAKE_RC=0).
