# VERDICT — DF-3734

**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`). The defect itself is **source-certain**; the trace below is the
evidence.

## Root cause

`r600_pm_get_dynpm_state()`, discrete-ASIC branch (non-IGP, non-R600),
`DYNPM_ACTION_DOWNCLOCK`:

```c
sys/dev/drm/radeon/r600.c:469-487
	case DYNPM_ACTION_DOWNCLOCK:
		if (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index) {
			if (rdev->pm.current_clock_mode_index == 0) {
				rdev->pm.requested_clock_mode_index = 0;      // <-- can be 0
				rdev->pm.dynpm_can_downclock = false;
			} else
				rdev->pm.requested_clock_mode_index =
					rdev->pm.current_clock_mode_index - 1;
		} else {
			rdev->pm.requested_clock_mode_index = 0;          // <-- can be 0
			rdev->pm.dynpm_can_downclock = false;
		}
		if ((rdev->pm.active_crtc_count > 0) &&
		    (rdev->pm.power_state[rdev->pm.requested_power_state_index].
		     clock_info[rdev->pm.requested_clock_mode_index].flags &
		     RADEON_PM_MODE_NO_DISPLAY)) {
			rdev->pm.requested_clock_mode_index++;             // :486 UNCHECKED
		}
```

The increment at `r600.c:486` has no comparison against
`power_state[requested_power_state_index].num_clock_modes`.

## Why the index can be out of bounds

`struct radeon_power_state.clock_info` is a **per-state heap pointer**
(`radeon.h:1300-1311`), sized by the BIOS table parser. For PowerPlayInfo
frev 1/2 (r6xx-era BIOSes) the parser gives **every** state exactly one mode:

```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);  // 1 element
	}
	...
	rdev->pm.power_state[state_index].num_clock_modes = 1;
```

With `num_clock_modes == 1`, `current_clock_mode_index` is necessarily 0, so
`requested_clock_mode_index` is always 0 entering the NO_DISPLAY check. The
`RADEON_PM_MODE_NO_DISPLAY` flag is set by default on state 0's single mode
(`radeon_atombios.c:2075-2078`), and the discrete branch selects
`requested_power_state_index = 1` (single-head, `r600.c:461-462`) or the
multi-head fallback 0 (`r600.c:458-460`). If the selected state's mode 0
carries NO_DISPLAY, the `++` yields index **1 == num_clock_modes** — exactly
one past the end of a 1-element allocation.

## OOB consumers (the read sites)

1. `r600.c:516-522` — `DRM_DEBUG_DRIVER("Requested: e: %d m: %d p: %d\n",
   power_state[req_ps].clock_info[req_cm].sclk, ... .mclk, ...)` — C function
   arguments are evaluated **even when debug printing is disabled** →
   unconditional OOB heap read.
2. `radeon_pm.c:181-199` (`radeon_set_power_state`) — `clock_info[req_cm].sclk`
   and `.mclk` OOB; the garbage values are clamped only *upward*
   (`> default_sclk`), then programmed via `radeon_set_engine_clock()` /
   `radeon_set_memory_clock()` (`radeon_pm.c:222-237`).
3. `r600_pm_misc()` `r600.c:766-783` — `ps->clock_info[req_cm].voltage` OOB;
   a garbage `VOLTAGE_SW` voltage is passed to `radeon_atom_set_voltage()`
   (`r600.c:777-778`) → arbitrary voltage index programmed to the GPU.

Impact ceiling: OOB kernel heap read (limited scope, kmalloc slab adjacent
data) whose contents drive GPU clock/voltage programming — local DoS /
hardware misbehavior class. No direct kernel-memory disclosure to userspace
(values go to GPU registers), hence Low severity — consistent with the
pass-1 rating of the sibling DF-1196 (Low).

## Reachability analysis (DragonFly-specific)

- `r600_pm_get_dynpm_state` is only called via `radeon_pm_get_dynpm_state`
  from the PM worker under `pm_method == PM_METHOD_DYNPM`
  (`radeon_pm.c:1765/1777/1800/1948`), all behind
  `radeon_pm_compute_clocks_old`'s `num_power_states < 2` guard
  (`radeon_pm.c:1735`).
- The only write of `PM_METHOD_DYNPM` is the sysfs store
  `radeon_set_pm_method` (`radeon_pm.c:444-449`), and **no sysfs power
  attributes are created on DragonFly** — both creation blocks are `#if 0`'d
  (`radeon_pm.c:1603-1616` for the DPM attrs, `1638-1647` for
  power_profile/power_method). `pm_method` is only ever PROFILE (default) or
  DPM (`radeon_pm.c:1522-1588`).
- Therefore on this tree the DYNPM branch is **dead code today** — the defect
  is latent and becomes live the moment the sysfs knob is re-enabled (a
  one-line `#if 1`), or in upstream-derived trees. This is exactly the same
  reachability posture as the accepted DF-1196 (same entry path), filed as a
  finding by pass 1 for the same reason.

## Guest verification performed

`env.txt` / `run.log`: DragonFly 6.5-DEVELOPMENT x86_64 guest;
`ls /boot/kernel/radeonkms.ko` → No such file (driver not built for this
guest kernel; only the 224 `radeonkmsfw_*.ko` firmware stubs exist);
`pciconf -l | grep 1002` → no AMD device (virtio-only). A runtime trigger is
impossible on this guest; no kernel build/fix validation was attempted for
the same reason (`fix_status: not_testable`).

## Recommended fix

Bounds-check the increment at `r600.c:486` (see `fix.diff`):
only increment while
`requested_clock_mode_index < power_state[requested_power_state_index].num_clock_modes - 1`.

## 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.
