⬢ DragonFlyBSD Kernel Audit
DF-3735 / fix.diff
← back to finding ↓ download raw
fix.diff — DF-3735: clamp requested_clock_mode_index at the consumer choke point
(radeon_set_power_state) so hardcoded profile cm indices (r600.c:685 et seq.)
and the DYNPM increment (r600.c:486) can never index past the selected power
state's clock_info allocation. Authored against the read-only sys/ tree;
never applied there.

diff --git a/sys/dev/drm/radeon/radeon_pm.c b/sys/dev/drm/radeon/radeon_pm.c
--- a/sys/dev/drm/radeon/radeon_pm.c
+++ b/sys/dev/drm/radeon/radeon_pm.c
@@ -175,9 +175,20 @@ static void radeon_set_power_state(struct radeon_device *rdev)
 	if ((rdev->pm.requested_clock_mode_index == rdev->pm.current_clock_mode_index) &&
 	    (rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index))
 		return;
 
+	/*
+	 * Clamp the requested clock mode to the selected power state's mode
+	 * count.  The r6xx/r7xx profile tables (r600_pm_init_profile) hardcode
+	 * clock-mode indices up to 2 and the DYNPM downclock path can
+	 * increment past the last mode, while PowerPlayInfo frev1/2 tables
+	 * give every state exactly one clock mode — without this clamp both
+	 * paths read past the end of the per-state clock_info allocation
+	 * (OOB heap read feeding clock/voltage programming).
+	 */
+	if ((rdev->pm.requested_power_state_index < rdev->pm.num_power_states) &&
+	    (rdev->pm.power_state[rdev->pm.requested_power_state_index].num_clock_modes > 0) &&
+	    (rdev->pm.requested_clock_mode_index >=
+	     rdev->pm.power_state[rdev->pm.requested_power_state_index].num_clock_modes))
+		rdev->pm.requested_clock_mode_index =
+			rdev->pm.power_state[rdev->pm.requested_power_state_index].num_clock_modes - 1;
+
 	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;