# VERDICT — DF-3735

**status: inconclusive** (runtime reproduction impossible on this audit guest:
virtio-only, no AMD PCI device, no `radeonkms.ko` in the guest kernel build —
`env.txt`/`run.log`). The defect is **source-certain**; trace below.

## Root cause

`r600_pm_init_profile()`, non-R600 branch, hardcodes clock-mode indices up to
2 for the profiles table without validating the selected power state's
`num_clock_modes`:

```c
sys/dev/drm/radeon/r600.c:679-685
	} else {
		if (rdev->pm.num_power_states < 4) {
			/* default */
			rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
			rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
			rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0;
			rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 2;      // <-- hardcoded 2
```

(further hardcoded `cm_idx` of 1 and 2 at r600.c:695, 700, 710, 715, 721,
735, 741, 750, 755, 761 for LOW/MID/HIGH SH/MH profiles.)

The indices are consumed unconditionally in the **default** configuration:

```c
sys/dev/drm/radeon/radeon_pm.c:133-143 (radeon_pm_update_profile)
	} else {
		rdev->pm.requested_power_state_index =
			rdev->pm.profiles[rdev->pm.profile_index].dpms_on_ps_idx;
		rdev->pm.requested_clock_mode_index =
			rdev->pm.profiles[rdev->pm.profile_index].dpms_on_cm_idx;   // = 2
	}
```

`pm.profile = PM_PROFILE_DEFAULT` out of the box (`radeon_pm.c:1359`,
`radeon_pm_init_old`), `pm_method = PM_METHOD_PROFILE` is the default on
DragonFly when DPM is off (`radeon_pm.c:1584-1587`), and
`radeon_pm_compute_clocks_old` runs `update_profile()+set_clocks()` for the
profile method on every modeset/DPMS change (`radeon_pm.c:1753-1756`, guard
`num_power_states >= 2` at `radeon_pm.c:1735`).

## Why index 2 is out of bounds on real r6xx hardware

`power_state[ps].clock_info` is a **per-state heap pointer**
(`radeon.h:1300-1311`). For PowerPlayInfo frev 1/2 tables (r6xx-era BIOSes —
the exact ASICs this file's discrete branch covers: RV610/RV630/RV635/RV670/
RV620), the parser allocates **exactly one** element per state and sets
`num_clock_modes = 1`:

```c
sys/dev/drm/radeon/radeon_atombios.c:2126-2133
	if (!rdev->pm.power_state[state_index].clock_info) {
		rdev->pm.power_state[state_index].clock_info =
			kzalloc(sizeof(struct radeon_pm_clock_info), GFP_KERNEL);
	}
	...
	rdev->pm.power_state[state_index].num_clock_modes = 1;
```

So on the first `radeon_pm_set_clocks()` with an active display:

```c
sys/dev/drm/radeon/radeon_pm.c:180-182 (radeon_set_power_state)
	if (radeon_gui_idle(rdev)) {
		sclk = rdev->pm.power_state[rdev->pm.requested_power_state_index].
			clock_info[rdev->pm.requested_clock_mode_index].sclk;   // clock_info[2] on a
		                                                               // 1-element alloc
```

The early-return at `radeon_pm.c:176-178` does not save it:
`current_clock_mode_index` starts at 0 (kzalloc'd rdev) ≠ 2, so the read
executes.

## OOB consumers and impact

1. `radeon_pm.c:181-182` `.sclk`, `radeon_pm.c:198-199` `.mclk` — OOB heap
   reads; values clamped only *upward* (`> default_sclk/default_mclk`), then
   programmed via `radeon_set_engine_clock()` / `radeon_set_memory_clock()`
   (`radeon_pm.c:222-237`) → out-of-spec/garbage GPU clocks from adjacent
   slab contents.
2. `r600_pm_misc()` `r600.c:766-783` — `ps->clock_info[req_cm_idx].voltage`
   OOB; garbage `VOLTAGE_SW` voltage → `radeon_atom_set_voltage()`
   (`r600.c:777-778`) → arbitrary voltage selector programmed.

Impact ceiling: default-configuration OOB kernel heap read (kmalloc slab
adjacent data) whose contents drive GPU clock/voltage programming — local
DoS / hardware misbehavior; no direct kernel-memory disclosure to userspace
(read values are written to GPU registers, not copied out). Medium severity,
config-dependent (needs frev1/2 BIOS hardware in the supported r6xx fleet).

## Negative checks performed

- The CHIP_R600 branch (`r600.c:642-678`) uses only cm 0 — safe.
- IGP RS780/RS880 use `rs780_pm_init_profile` (`radeon_asic.c:1166`) which
  uses only cm 0 — safe.
- pplib (frev ≥ 4 / parser 2) BIOSes allocate multi-mode `clock_info`
  arrays (`radeon_atombios.c:2608, 2704`) — not vulnerable; the exposure is
  specific to the frev1/2 parser path.
- COMBIOS r6xx is impossible (this file requires ATOMBIOS, `r600.c:3272-3276`).

## Guest verification performed

`env.txt`/`run.log`: DragonFly 6.5-DEVELOPMENT x86_64; no `radeonkms.ko` in
`/boot/kernel` (224 `radeonkmsfw_*.ko` firmware stubs only, including
R600/RV61x/RV63x/RV670 firmware — the stack targets this hardware family);
`pciconf -l` shows no AMD device. Runtime trigger impossible on this guest;
no kernel fix build attempted for the same reason (`fix_status:
not_testable`).

## Recommended fix

Clamp every hardcoded profile `*_cm_idx` to the target power state's mode
count at profile-init time — see `fix.diff` (guards on
`power_state[ps].num_clock_modes` before assigning 1/2).

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
