# DF-1401 — trinity_parse_power_table nonClockInfoIndex OOB read

## Verdict: REPRODUCED (primitive proven via source trace + byte-exact harness). Fix compiles.

`trinity_parse_power_table` indexes the non-clock info array with a
VBIOS-supplied `nonClockInfoIndex` (u8) and **no bounds check**, unlike the
clock path in the same function which IS guarded. A crafted VBIOS with
`nonClockInfoIndex >= ucNumEntries` reads out of bounds. This is a **read-only
primitive** (the OOB data is read and stored, not written through), so there is
no escalation chain — the impact ceiling is OOB-info read / wrong DPM settings
(DoS via bogus clock programming). Confirmed real by source tracing + harness.
**Not live-reachable on the QEMU guest** (no AMD Trinity GPU); the fix compiles.

## Mechanism (trigger → primitive → effect)

`sys/dev/drm/radeon/trinity_dpm.c:1774-1776`:
```c
non_clock_array_index = power_state->v2.nonClockInfoIndex;   /* u8 from VBIOS, NO CHECK */
non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
    &non_clock_info_array->nonClockInfo[non_clock_array_index];   /* OOB READ */
```

Contrast the **clock** path at `:1786-1790`, which IS guarded:
```c
idx = (u8 *)&power_state->v2.clockInfoIndex[0];
for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) {
    clock_array_index = idx[j];
    if (clock_array_index >= clock_info_array->ucNumEntries)   /* <-- GUARDED */
        continue;
    ...
}
```
The non-clock index has no equivalent check — an oversight sibling of
DF-1269/1333.

The array type (`sys/dev/drm/radeon/pptable.h:456-464`):
```c
typedef struct _NonClockInfoArray {
    UCHAR ucNumEntries;
    UCHAR ucEntrySize;
    ATOM_PPLIB_NONCLOCK_INFO nonClockInfo[1];   /* array of structs; index is element index */
} NonClockInfoArray;
```

`non_clock_info` is then passed to `trinity_parse_pplib_non_clock_info(...)` and
its fields are READ into the power-state struct.

## Primitive

- **Class:** out-of-bounds READ, VBIOS-controlled index (u8, 0..255) vs
  `ucNumEntries`; with `ucNumEntries=1` and index 255, reads ~255 entries past
  the array → up to ~`255*ucEntrySize` bytes OOB.
- **READ-ONLY:** no write-through, so this is NOT a memory-corruption write
  primitive. **Valid hard blocker for escalation** (Phase 6: read-only
  primitive → no chain). Impact ceiling = OOB-info read / wrong DPM settings
  → DoS via bogus clock programming on crafted VBIOS.

## Reachability / threat model

- `radeon` is a loadable module (`radeon.ko`), NOT in `X86_64_GENERIC`. It
  attaches to AMD/ATI Radeon GPUs. The QEMU guest has no AMD GPU, so
  `trinity_parse_power_table` never runs live here.
- Threat: a crafted VBIOS (e.g. flashed malicious firmware, or a malicious
  virtual-GPU passthrough) parsed at GPU attach. This is a local,
  already-on-the-box attacker with the ability to present a crafted VBIOS
  image to the driver.

## Harness proof (run.log)

```
nonClockInfoArray.ucNumEntries = 1
nonClockInfoIndex (from VBIOS) = 200
guard present? : NO (contrast clock path at :1789 which checks >= ucNumEntries)
reads entry [200] of a 1-entry array -> OUT OF BOUNDS
OOB READ CONFIRMED: nonClockInfoIndex=200 used against ucNumEntries=1 with no bounds check; reads ~3200 bytes OOB.
```

## Fix validation

`fix.diff` adds the missing guard mirroring the clock path: after reading
`nonClockInfoIndex`, if it `>= non_clock_info_array->ucNumEntries`, free
`rdev->pm.dpm.ps`, set it NULL, and `return -EINVAL` (matching the existing
error-cleanup pattern at `:1780-1783` which frees `dpm.ps` and returns on
allocation failure). The fix was applied to in-guest `/usr/src` and the
`radeon.ko` module rebuilt cleanly (`cc ... -Werror`, RC=0; `trinity_dpm.o`
built with the fix). Runtime re-test is not possible on this guest (no AMD
GPU), so `fix_status = not_testable` for runtime, with the diff verified to
**apply + compile** and the harness logic confirming the guard rejects the OOB
index.

## Kernel references

- `sys/dev/drm/radeon/trinity_dpm.c:1774-1776` (unguarded non-clock index)
- `sys/dev/drm/radeon/trinity_dpm.c:1786-1790` (the guarded clock path it should mirror)
- `sys/dev/drm/radeon/pptable.h:456-464` (`struct _NonClockInfoArray`)
