⬢ DragonFlyBSD Kernel Audit
DF-4146 / fix.diff
← back to finding ↓ download raw
--- a/dev/drm/radeon/trinity_dpm.c
+++ b/dev/drm/radeon/trinity_dpm.c
@@ -1747,11 +1747,21 @@
 	u8 frev, crev;
 	u8 *power_state_offset;
 	struct sumo_ps *ps;
+	u16 pp_size;
 
 	if (!atom_parse_data_header(mode_info->atom_context, index, NULL,
 				   &frev, &crev, &data_offset))
 		return -EINVAL;
+	if (!atom_parse_data_header(mode_info->atom_context, index, &pp_size,
+				   &frev, &crev, &data_offset))
+		return -EINVAL;
 	power_info = (union power_info *)((uint8_t*)mode_info->atom_context->bios + data_offset);
+	if ((u32)(le16_to_cpu(power_info->pplib.usStateArrayOffset) + 2) > pp_size ||
+	    (u32)(le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) + 3) > pp_size ||
+	    (u32)(le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) + 3) > pp_size) {
+		DRM_ERROR("invalid powerplay array offsets\n");
+		return -EINVAL;
+	}
 
 	state_array = (struct _StateArray *)
 		((uint8_t*)mode_info->atom_context->bios + data_offset +
@@ -1771,6 +1781,17 @@
 	for (i = 0; i < state_array->ucNumEntries; i++) {
 		u8 *idx;
 		power_state = (union pplib_power_state *)power_state_offset;
+		if ((u32)(power_state_offset + 2 + power_state->v2.ucNumDPMLevels -
+			  (u8 *)power_info) > pp_size) {
+			DRM_ERROR("state walk out of bounds at %d\n", i);
+			for (k = i - 1; k >= 0; k--)
+				kfree(rdev->pm.dpm.ps[k].ps_priv);
+			kfree(rdev->pm.dpm.ps);
+			rdev->pm.dpm.ps = NULL;
+			rdev->pm.dpm.boot_ps = NULL;
+			rdev->pm.dpm.uvd_ps = NULL;
+			return -EINVAL;
+		}
 		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];