# DF-3734 — unchecked `requested_clock_mode_index++` in r600 DYNPM downclock (discrete branch)

**File:** `sys/dev/drm/radeon/r600.c:482-487`
**Class:** kernel OOB heap read (one-past-end) — CWE-125 / CWE-1284
**Severity:** Low · **Confidence:** likely (source-certain defect; trigger gated on DYNPM)
**Bucket:** kernleak (sibling of DF-1196, distinct site — see "not a duplicate" below)

## Build / Run / Expected

There is no runnable userspace trigger: the bug is an internal index arithmetic
defect on the kernel PM worker path and requires a physical r6xx/r7xx GPU with a
specific ATOM BIOS PowerPlay table, plus `pm_method=dynpm`. The guest used for
this audit is virtio-only (no AMD PCI device) and its kernel build ships no
`radeonkms.ko` — see `env.txt`. The verification performed is therefore
**source-level** (full trace in `VERDICT.md`) plus a guest module/hardware
presence check (re-run any time):

```sh
dfbsd-qemu/vm.sh run_root 'ls /boot/kernel/radeonkms.ko; pciconf -l | grep 1002'
```

**Expected result on real hardware** (RV610/RV630/RV635/RV670 etc. with
PowerPlayInfo frev1/2 BIOS, ≥2 power states, display connected, DYNPM enabled):
`power_state[req_ps].clock_info[1]` is read one element past a 1-element
`kzalloc` — at `r600.c:516-522` (DRM_DEBUG_DRIVER args, evaluated even with
debug printing off) and via `r600_pm_misc()` at `r600.c:771` /
`radeon_pm.c:181-199` → garbage `sclk/mclk/voltage` programmed to the GPU.

## Trigger conditions (all required)

1. `pm_method == PM_METHOD_DYNPM` — on DragonFly this is **currently
   unreachable**: the only transitions to DYNPM are the sysfs store
   `radeon_set_pm_method` (`radeon_pm.c:444-449`), and *all* sysfs attribute
   creation is compiled out (`#if 0` at `radeon_pm.c:1603-1616` and
   `1638-1647`). The defect is latent in this tree, live in upstream-style
   configs.
2. Non-IGP, non-R600 ASIC (RV610..RV770 discrete) → the `else` branch of
   `r600_pm_get_dynpm_state` (`r600.c:442-514`).
3. `DYNPM_ACTION_DOWNCLOCK` with `requested_clock_mode_index == 0`
   (always true when the state's `num_clock_modes == 1`).
4. `active_crtc_count > 0` and `clock_info[0].flags & RADEON_PM_MODE_NO_DISPLAY`
   (set by default on non-default state 0 — `radeon_atombios.c:2075-2078`).
5. Selected power state built by the frev1/2 parser → **every** state has
   `num_clock_modes == 1` and a 1-element `clock_info` allocation
   (`radeon_atombios.c:2127-2133`).

Then `requested_clock_mode_index++` (r600.c:486) makes the index 1 == the
state's mode count → one-past-end.

## Why this is NOT a re-report of DF-1196

DF-1196 covers the **power_state** index increment at `r600.c:405` in the
IGP/R600 branch (`requested_power_state_index++` vs `num_power_states`).
DF-3734 is the **clock_mode** index increment at `r600.c:486` in the
discrete-ASIC branch (`requested_clock_mode_index++` vs `num_clock_modes`) —
different variable, different array (`clock_info` is a per-state heap
allocation, `power_state` the outer array), different branch, different
bounds source.

## Fix

`fix.diff` (bounds-check the increment). Never applied to `sys/` — authored
against the read-only tree.

## Artifacts

- `VERDICT.md` — full source-level proof and reachability analysis
- `run.sh`, `run.log`, `env.txt` — guest module/hardware presence check
- `fix.diff` — proposed fix
- `manifest.json`, `verdict.json`
