# DF-1402 — trinity_parse_power_table VCE clk_idx OOB read

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

In the VCE power-state loop of `trinity_parse_power_table`, a VBIOS-supplied
6-bit `clk_idx` (0..63) indexes the clock info array with **no bounds check**
— matching exactly the finding's claim of ~1008-byte OOB read. Read-only
primitive (no write-through), so no escalation chain; ceiling = OOB-info read /
wrong VCE clock (DoS). 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:1816-1823`:
```c
/* fill in the vce power states */
for (i = 0; i < RADEON_MAX_VCE_LEVELS; i++) {
    u32 sclk;
    clock_array_index = rdev->pm.dpm.vce_states[i].clk_idx;   /* 6-bit 0..63, NO CHECK */
    clock_info = (union pplib_clock_info *)
        &clock_info_array->clockInfo[clock_array_index * clock_info_array->ucEntrySize];  /* OOB */
    sclk = le16_to_cpu(clock_info->sumo.usEngineClockLow);
    sclk |= clock_info->sumo.ucEngineClockHigh << 16;
    rdev->pm.dpm.vce_states[i].sclk = sclk;                  /* stored OOB-read value */
    ...
}
```

The array type (`sys/dev/drm/radeon/pptable.h:446-454`):
```c
typedef struct _ClockInfoArray {
    UCHAR ucNumEntries;
    UCHAR ucEntrySize;
    UCHAR clockInfo[1];        /* BYTE array (UCHAR) */
} ClockInfoArray;
```
Because `clockInfo` is a **byte array**, `[clock_array_index * ucEntrySize]` is
a **byte offset** — with `clk_idx=63` and `ucEntrySize=16`, that is byte offset
1008; against `ucNumEntries=1` (16 bytes) it reads ~992 bytes OOB. The read
value is stored into `vce_states[i].sclk`.

The clock path at `:1786-1790` guards `clock_array_index >= ucNumEntries`, but
this VCE loop does not — an oversight sibling of DF-1269/1307.

## Primitive

- **Class:** out-of-bounds READ, VBIOS-controlled 6-bit index (0..63) vs
  `ucNumEntries`; up to ~1008 bytes OOB per the finding.
- **READ-ONLY:** no write-through → NOT a write primitive. **Valid hard
  blocker** for escalation (Phase 6: read-only → no chain). Impact ceiling =
  wrong VCE engine clock / OOB info read → DoS via bogus clock programming.

## Reachability / threat model

- `radeon` is a loadable module (`radeon.ko`), NOT in `X86_64_GENERIC`.
  Attaches to AMD/ATI Radeon GPUs. No AMD GPU on the QEMU guest → not
  live-reachable here.
- Threat: crafted VBIOS parsed at GPU attach (malicious firmware flash / GPU
  passthrough). Local, already-on-the-box attacker presenting a crafted VBIOS.

## Harness proof (run.log)

```
clockInfoArray.ucNumEntries = 1, ucEntrySize = 16
  level 0: clk_idx=63 -> byte offset [63*16=1008] OOB -> sclk=0xdddddd
  ...
guard present? : NO (clock path at :1789 checks, this VCE loop does not)
OOB READ CONFIRMED: clk_idx=63 vs ucNumEntries=1 reads ~1008 bytes OOB per level (idx*ucEntrySize).
```
The harness initially segfaulted because `clockInfo` was wrongly modelled as an
element array; the kernel `pptable.h` shows it is a `UCHAR[]` byte array, so
the index is a byte offset — exactly the 1008-byte OOB the finding describes.

## Fix validation

`fix.diff` adds the missing guard mirroring the clock path: after reading
`clk_idx`, if `clock_array_index >= clock_info_array->ucNumEntries`, `continue`
(skip that VCE level — no allocation in the loop, so `continue` is clean, and
it matches the `:1789` `continue` style). The fix was applied to in-guest
`/usr/src` and `radeon.ko` rebuilt cleanly (`cc ... -Werror`, RC=0;
`trinity_dpm.o` built with the fix). Runtime re-test not possible (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:1816-1823` (unguarded VCE clk_idx loop)
- `sys/dev/drm/radeon/trinity_dpm.c:1786-1790` (the guarded clock path it should mirror)
- `sys/dev/drm/radeon/pptable.h:446-454` (`struct _ClockInfoArray`, byte-array `clockInfo`)
