DragonFlyBSD Kernel Audit
DF-1198 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/drm/radeon/radeon_atombios.c b/sys/dev/drm/radeon/radeon_atombios.c
--- a/sys/dev/drm/radeon/radeon_atombios.c
+++ b/sys/dev/drm/radeon/radeon_atombios.c
@@ -2701,11 +2701,11 @@
 		non_clock_array_index = power_state->v2.nonClockInfoIndex;
 		non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
 			&non_clock_info_array->nonClockInfo[non_clock_array_index];
-		rdev->pm.power_state[i].clock_info = kzalloc(sizeof(struct radeon_pm_clock_info) *
+		rdev->pm.power_state[state_index].clock_info = kzalloc(sizeof(struct radeon_pm_clock_info) *
 							     (power_state->v2.ucNumDPMLevels ?
 							      power_state->v2.ucNumDPMLevels : 1),
 							     GFP_KERNEL);
-		if (!rdev->pm.power_state[i].clock_info)
+		if (!rdev->pm.power_state[state_index].clock_info)
 			return state_index;
 		if (power_state->v2.ucNumDPMLevels) {
 			for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) {
@@ -2730,6 +2730,17 @@
 			radeon_atombios_parse_pplib_non_clock_info(rdev, state_index, mode_index,
 								   non_clock_info);
 			state_index++;
+		} else {
+			/* DF-1198: this state produced no valid clock modes, so it is not
+			 * compacted into the power_state[] array. Free its clock_info so
+			 * the state_index slot can be reused by the next iteration.
+			 * Previously clock_info was allocated against index i while every
+			 * write used state_index; once state_index lagged i (a prior state
+			 * with zero valid modes), the next state's writes landed in a
+			 * buffer sized for a different state -> heap overflow. Allocating
+			 * and writing against the same index closes the overflow. */
+			kfree(rdev->pm.power_state[state_index].clock_info);
+			rdev->pm.power_state[state_index].clock_info = NULL;
 		}
 		power_state_offset += 2 + power_state->v2.ucNumDPMLevels;
 	}