# DF-1200 — radeon unvalidated BIOS-supplied power-table indices

## Verdict
**NOT REPRODUCED (source-confirmed; hardware+config-gated).** The bug is real
in both the v1 (parse_power_table_4_5) and v2 (parse_power_table_6) parsers,
but the radeon driver is **not** in `X86_64_GENERIC` and no AMD/ATI GPU is
present on this QEMU/KVM guest (vgapci0 is QEMU std VGA, vendor 0x1234). No
runtime trigger; validated by line-level source trace + single-fix kernel
build.

## Mechanism
`radeon_atombios_parse_power_table_4_5` (`sys/dev/drm/radeon/radeon_atombios.c:2570`)
and `_6` (`radeon_atombios.c:2658`) parse the VBIOS PowerPlayInfo table. In
both, BIOS-supplied `u8` indices are used as direct multipliers against the
per-entry sizes to compute offsets into the `nonClockInfo` / `clockInfo`
arrays **without any bounds check**:

* v1, `radeon_atombios.c:2606`:
  `(power_state->v1.ucNonClockStateIndex * power_info->pplib.ucNonClockSize)`
* v1, `radeon_atombios.c:2619`:
  `(power_state->v1.ucClockStateIndices[j] * power_info->pplib.ucClockInfoSize)`
* v2, `radeon_atombios.c:2701-2703`:
  `non_clock_array_index = power_state->v2.nonClockInfoIndex; ...->nonClockInfo[non_clock_array_index]`
* v2, `radeon_atombios.c:2712-2714`:
  `clock_array_index = power_state->v2.clockInfoIndex[j]; ...->clockInfo[clock_array_index * clock_info_array->ucEntrySize]`

With `u8` indices up to 255 and per-entry sizes of ~40 bytes
(`ATOM_PPLIB_NONCLOCK_INFO`) and ~16-24 bytes (clock info), a malformed or
hostile VBIOS image can drive a kernel-pointer dereference 15 KB – 65 KB past
the array into the mapped VBIOS shadow / kernel heap. The data read flows
into `rdev->pm.power_state[].clock_info[].sclk/mclk/vddc` which is then
exposed via DRM debugfs / sysfs — a kernel-memory info leak, and on platforms
where the OOB read lands on an unmapped page, a kernel panic at attach.

The array bounds ARE available: v2 has explicit `_StateArray.ucNumEntries`,
`_ClockInfoArray.ucNumEntries`, `_NonClockInfoArray.ucNumEntries`
(`pptable.h:438-464`). For v1 the spec mandates the non-clock array carries
one entry per state, so `ucNumStates` (`pptable.h:153`) is the natural bound.

## Why not triggered on this guest
Two independent reasons:

1. `radeon` is **not** in `X86_64_GENERIC` — `grep radeon sys/config/X86_64_GENERIC`
   returns nothing. Loading it requires `kldload radeon` which needs root,
   and the only GPU on the guest is QEMU std VGA (vendor 0x1234, not AMD/ATI),
   so the radeon PCI attachment table would never claim it.
2. No AMD/ATI GPU is present, so even with `radeon.ko` loaded there is no
   radeon device to attach and parse a VBIOS for.

Option (d). To exercise the path at runtime you would need real AMD hardware
(or a sufficiently faithful PCI stub presenting a forged ATOM BIOS image).

## Recommended fix (in `fix.diff`)
Validate each index before use:

* v1: `ucNonClockStateIndex` and `ucClockStateIndices[j]` must be `< ucNumStates`.
* v2: `nonClockInfoIndex` must be `< non_clock_info_array->ucNumEntries`;
  `clockInfoIndex[j]` must be `< clock_info_array->ucNumEntries`.

On violation the state is skipped (`continue` in v1; `goto next_state_v6` in
v2 — the v2 goto is necessary because the outer loop advances
`power_state_offset` by a per-state stride and `continue` would skip that
advance).

## Build validation
Cumulative kernel build with all 5 fixes applied — `NK_DONE rc=0`. See
`fix_build.log`. Note: the radeon module is compiled as part of the DRM
modules (not the base kernel), but `nativekernel` does build every enabled
module; the build log shows the modified `radeon_atombios.c` compiled without
errors or warnings.

## Reproduce
```
./build.sh    # no-op
./run.sh      # no-op
```
