# DF-1138 — Verdict

## Verdict: REPRODUCED (source-level + harness) — OOB read of BIOS mapping / info leak, no escalation chain (read-only primitive)

## Bug confirmation

`si_parse_power_table` (radeon/si_dpm.c:6808-6897) contains two loops
that read entries from the VBIOS `clockInfoArray`:

- **The main power-state loop at line 6864-6877** — properly guards:
  ```c
  for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) {
      clock_array_index = idx[j];
      if (clock_array_index >= clock_info_array->ucNumEntries)
          continue;                                         /* line 6866 */
      ...
      clock_info = (union pplib_clock_info *)
          ((u8 *)&clock_info_array->clockInfo[0] +
           (clock_array_index * clock_info_array->ucEntrySize));
  ```

- **The VCE-state fill-in loop at line 6883-6894** — missing the guard:
  ```c
  for (i = 0; i < RADEON_MAX_VCE_LEVELS; i++) {
      u32 sclk, mclk;
      clock_array_index = rdev->pm.dpm.vce_states[i].clk_idx;   /* from BIOS */
      /* NO check against clock_info_array->ucNumEntries */
      clock_info = (union pplib_clock_info *)
          &clock_info_array->clockInfo[clock_array_index * clock_info_array->ucEntrySize];
      sclk = le16_to_cpu(clock_info->si.usEngineClockLow);
      sclk |= clock_info->si.ucEngineClockHigh << 16;
      ...
      rdev->pm.dpm.vce_states[i].sclk = sclk;     /* stored for later readback */
  }
  ```

`rdev->pm.dpm.vce_states[i].clk_idx` is populated at **r600_dpm.c:1127-1128**
from the BIOS `state_entry->ucClockInfoIndex & 0x3f` — a 6-bit value
range 0..63. With `ucNumEntries` typically small (5-10 in real VBIOSes),
a crafted `ucClockInfoIndex` of `0x3f` makes `clock_array_index=63`, and
the read fetches `clockInfo[63 * ucEntrySize]` — up to ~1 KB past the
end of the BIOS `clockInfoArray` mapping.

The OOB-read `sclk`/`mclk` are stored into `rdev->pm.dpm.vce_states[i]`
and are subsequently readable from userspace via the `radeon_pm_info`
debugfs / sysfs path. → info leak of BIOS mapping contents.

## Harness confirmation

`harness.c` constructs a `clockInfoArray` with `ucNumEntries=5`,
`ucEntrySize=16` and a `vce_state.clk_idx=0x3f` (the max possible from
the BIOS mask). The buggy VCE-loop path reads byte `0xab` at offset
1008 into `clockInfo` — 928 bytes past the in-bounds region. The fixed
path rejects the same `clk_idx` with the missing guard restored.

Output captured in `run.log`:
```
[A] Main-loop guard (si_dpm.c:6866) on same clk_idx:
    buggy code path returns -1 (out-of-range rejected)
    rc=-1  (guarded)

[B] VCE-loop WITHOUT guard (si_dpm.c:6885-6887) -- the bug:
    read byte=0xab at offset clockInfo[1008] (=1008 bytes into clockInfo)
    >>> In kernel: OOB read of BIOS mapping by 928 bytes (0x3f0 - 0x50 = 928) <<<
    >>> Real-world: a malicious VBIOS can read up to 0x3f * 16 = 1008 bytes OOB <<<

[C] VCE-loop WITH fix.diff guard:
    rc=-1  (rejected, no OOB read)
```

## Exploit chain

Read-only primitive. The leaked data is BIOS-mapping bytes (typically
the `clockInfoArray` and adjacent `nonClockInfoArray` data, then VBIOS
code/data segments if the offset is large). Not directly a kernel-memory
leak — the BIOS mapping is a fixed `iomap`/`memcpy`'d region, not
arbitrary kernel heap.

Realistic impact ceiling:
- **Info leak of BIOS mapping contents** up to ~1 KB past the
  `clockInfoArray`, readable via `radeon_pm_info`.
- **Panic** if the OOB offset crosses into an unmapped page (unlikely —
  BIOS mappings are usually backed by a contiguous ROM BAR, but a
  minimal crafted VBIOS image could trip this).

No escalation chain. The bug's impact is info leak / DoS.

## Trigger conditions (not met on this guest)

1. AMD SI GPU present (no AMD GPU on the QEMU audit guest).
2. `radeon.ko` loaded.
3. Crafted VBIOS where any `ATOM_PPLIB_VCE_State_Record.ucClockInfoIndex`
   has its low 6 bits >= `clockInfoArray.ucNumEntries`.

Source-level + harness-confirmed; no live runtime trigger on the guest.

## Fix

`fix.diff` adds the missing `ucNumEntries` bounds check inside the VCE
loop, mirroring the existing main-loop guard at line 6866:
```c
if (clock_array_index >= clock_info_array->ucNumEntries) {
    dev_warn(rdev->dev, "invalid vce clk_idx %d (>= %u)\n", ...);
    continue;
}
```

## Fix validation

1. `patch -p1 --check` — clean apply, 1 hunk.
2. `cd /usr/src/sys/dev/drm/radeon && make` with the diff applied —
   `radeon.ko` built cleanly (`rc=0`, 2,029,432 bytes).
3. Reverted.

Behaviour comparison at the harness level: `run.log` shows the buggy
VCE-loop reading OOB while the fixed path rejects the bad `clk_idx`.

`fix_status: fixed` (compiles cleanly, harness confirms the patched
code path rejects the bad input).
