r600_pm_init_profile hardcodes clock-mode indices without num_clock_modes validation β default-config OOB heap read of per-state clock_info
Summary
r600_pm_init_profile's non-R600 branch hardcodes clock-mode indices up to 2 - including dpms_on_cm_idx=2 for the DEFAULT profile - with no validation against the target power state's num_clock_modes. On r6xx discrete ASICs with PowerPlayInfo frev1/2 tables every state has exactly one clock mode and a 1-element clock_info allocation, so radeon_set_power_state reads clock_info[2].sclk/.mclk and r600_pm_misc reads .voltage two elements past the allocation; the garbage values are programmed as engine/memory clocks and VOLTAGE_SW voltage. Happens automatically - PM_PROFILE_DEFAULT and PM_METHOD_PROFILE are the out-of-the-box settings, invoked on every modeset with a display connected; no sysfs or DYNPM needed, unlike DF-1196/DF-3734. Impact: OOB kernel heap read feeding GPU clock/voltage programming - local DoS / out-of-spec hardware operation; no direct kernel-memory disclosure to userspace. Fix: clamp at the consumer choke point in radeon_set_power_state (covers hardcoded indices and the DYNPM increment).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3735 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.6 KB | β raw | |
| VERDICT.md | β | 5.3 KB | β raw | |
| run.sh | β | 129 B | view raw | |
| run.log | β | 227 B | view raw | |
| env.txt | β | 231 B | view raw | |
| fix.diff | β | 1.8 KB | view raw | |
| verdict.json | β | 3.9 KB | view raw |
DF-3735 β r600_pm_init_profile hardcodes clock-mode indices without validating num_clock_modes β OOB heap read in the default PM profile
File: sys/dev/drm/radeon/r600.c:680-763 (consumer sites r600.c:771, radeon_pm.c:181-199)
Class: kernel OOB heap read β CWE-125 / CWE-1284
Severity: Medium Β· Confidence: likely
Bucket: kernleak
Build / Run / Expected
Kernel-internal PM path β no userspace trigger exists without a physical
r6xx discrete GPU carrying a PowerPlayInfo frev1/2 BIOS (single clock mode
per state). The audit guest is virtio-only and its kernel build ships no
radeonkms.ko (env.txt). Verification is source-level (VERDICT.md) plus
the guest presence check:
dfbsd-qemu/vm.sh run_root 'ls /boot/kernel/radeonkms.ko; pciconf -l | grep 1002'
Expected on real hardware (e.g. RV630/RV635/RV670, frev1/2 PowerPlay,
β₯2 power states, display connected): on the first
radeon_pm_compute_clocks() after a modeset, the DEFAULT profile yields
requested_clock_mode_index = 2 while the selected power state's
clock_info allocation holds exactly one element β
power_state[ps].clock_info[2].sclk/.mclk and .voltage are read two
elements past the allocation; garbage values are programmed as GPU engine
clock, memory clock and (VOLTAGE_SW) voltage.
Trigger conditions
- Non-IGP, non-R600 r6xx/r7xx discrete ASIC (
r600_pm_init_profileelse branch,r600.c:679) β wired to RV610..RV770-family asic blocks (radeon_asic.c:987,1073,1272,1392). num_power_states < 4(r600.c:680) and β₯ 2 (radeon_pm.c:1735guard).- PowerPlayInfo frev 1/2 BIOS β every state:
num_clock_modes == 1, 1-elementclock_infoallocation (radeon_atombios.c:2126-2133). active_crtc_count > 0(display running) β DEFAULT profile'sdpms_on_cm_idx = 2is applied (radeon_pm.c:138-143).
No sysfs interaction and no DYNPM needed β PM_PROFILE_DEFAULT is the
out-of-the-box profile (radeon_pm.c:1359), and
radeon_pm_compute_clocks_old calls
radeon_pm_update_profile()+radeon_pm_set_clocks() for PM_METHOD_PROFILE
(radeon_pm.c:1753-1756), which is the default method on DragonFly when DPM
is off (radeon_pm.c:1584-1587).
Fix
fix.diff β clamp the profile-initialized clock-mode indices to each target
power state's num_clock_modes - 1 at profile-init time. Never applied to
sys/; authored against the read-only tree.
Artifacts
VERDICT.mdβ full source-level proofrun.sh,run.log,env.txtβ guest module/hardware presence checkfix.diffβ proposed fixmanifest.json,verdict.json
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:
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:
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:
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:
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
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 viaradeon_set_engine_clock()/radeon_set_memory_clock()(radeon_pm.c:222-237) β out-of-spec/garbage GPU clocks from adjacent slab contents.r600_pm_misc()r600.c:766-783βps->clock_info[req_cm_idx].voltageOOB; garbageVOLTAGE_SWvoltage β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_infoarrays (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.
Fix verification
not_testablefix.diff authored against the read-only sys/ tree; kernel fix build/validation not attempted β the guest has no radeon hardware and no radeonkms.ko, so neither baseline nor patched behavior is observable.
findings/poc/DF-3735/fix.diff
Confirmed kernel references
- sys/dev/drm/radeon/r600.c:680-715
- sys/dev/drm/radeon/r600.c:685
- sys/dev/drm/radeon/r600.c:766-783
- sys/dev/drm/radeon/radeon.h:1300-1311
- sys/dev/drm/radeon/radeon_atombios.c:2126-2133
- sys/dev/drm/radeon/radeon_pm.c:133-143
- sys/dev/drm/radeon/radeon_pm.c:171-199
- sys/dev/drm/radeon/radeon_pm.c:1753-1756
- sys/dev/drm/radeon/radeon_asic.c:987
Detail
Evidence (decisive lines)
VERDICT.md (full source-level trace with path:line); fix.diff (consumer-side clamp in radeon_set_power_state); run.log + env.txt (guest: no AMD GPU, no radeonkms.ko β 224 radeonkmsfw_* firmware stubs only, including R600/RV61x/RV63x/RV670 images); decisive lines: r600.c:685 (hardcoded dpms_on_cm_idx=2), radeon_pm.c:133-143 + 1753-1756 + 181-199 (default-config consumption), radeon.h:1300-1311 (clock_info per-state heap pointer), radeon_atombios.c:2126-2133 (1-element allocation, num_clock_modes=1).
PoC changes
No runnable PoC exists for a kernel-internal PM index bug without r6xx hardware; replaced with a source-level proof plus guest module/hardware presence check.
Verified recommended fix
Clamp requested_clock_mode_index to power_state[requested_power_state_index].num_clock_modes-1 in radeon_set_power_state before first use (covers the hardcoded profile indices and the DYNPM increment) β see fix.diff.
Verdict
Source-certain OOB heap read reachable in the DEFAULT power-management configuration: r600_pm_init_profile's non-R600 branch (r600.c:680-715) hardcodes clock-mode indices up to 2 (dpms_on_cm_idx=2 for PM_PROFILE_DEFAULT_IDX at r600.c:685) with no validation against the target power state's num_clock_modes. On r6xx discrete ASICs (RV610..RV670, wired at radeon_asic.c:987/1073/1272/1392) with PowerPlayInfo frev1/2 BIOS tables every state has exactly one clock mode and a 1-element clock_info allocation (radeon_atombios.c:2126-2133), so radeon_set_power_state reads power_state[ps].clock_info[2].sclk/.mclk (radeon_pm.c:181-199) and r600_pm_misc reads .voltage (r600.c:771) two elements past the allocation on the first set_clocks with an active display; the garbage values are programmed as engine/memory clocks and VOLTAGE_SW voltage. No sysfs or DYNPM needed β PM_PROFILE_DEFAULT + PM_METHOD_PROFILE are the out-of-the-box settings (radeon_pm.c:1359, 1584-1587), invoked from radeon_pm_compute_clocks_old at radeon_pm.c:1753-1756. Runtime reproduction was impossible on the audit guest: virtio-only (no AMD PCI device) and radeonkms.ko is not built for the guest kernel (env.txt/run.log).
No comments yet.