DragonFlyBSD Kernel Audit
DF-1200 / 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
@@ -2600,6 +2600,17 @@
 			(mode_info->atom_context->bios + data_offset +
 			 le16_to_cpu(power_info->pplib.usStateArrayOffset) +
 			 i * power_info->pplib.ucStateEntrySize);
+		/*
+		 * BIOS-supplied indices into the NonClockInfo / ClockInfo
+		 * arrays are u8 and used unvalidated as multipliers against
+		 * the per-entry sizes.  Cap them at ucNumStates -- the spec
+		 * mandates that the non-clock array carries one entry per
+		 * state -- so a hostile/malformed BIOS cannot drive an OOB
+		 * read up to ~64 KB into kernel heap (DF-1200).
+		 */
+		if (power_state->v1.ucNonClockStateIndex >=
+		    power_info->pplib.ucNumStates)
+			continue;
 		non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
 			(mode_info->atom_context->bios + data_offset +
 			 le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) +
@@ -2613,6 +2624,9 @@
 			return state_index;
 		if (power_info->pplib.ucStateEntrySize - 1) {
 			for (j = 0; j < (power_info->pplib.ucStateEntrySize - 1); j++) {
+				if (power_state->v1.ucClockStateIndices[j] >=
+				    power_info->pplib.ucNumStates)
+					break;
 				clock_info = (union pplib_clock_info *)
 					(mode_info->atom_context->bios + data_offset +
 					 le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) +
@@ -2699,6 +2713,14 @@
 		mode_index = 0;
 		power_state = (union pplib_power_state *)power_state_offset;
 		non_clock_array_index = power_state->v2.nonClockInfoIndex;
+		/*
+		 * Validate BIOS-supplied u8 indices against the explicit
+		 * ucNumEntries in each array header (DF-1200).  On violation
+		 * we still advance power_state_offset below so the parse
+		 * remains stable.
+		 */
+		if (non_clock_array_index >= non_clock_info_array->ucNumEntries)
+			goto next_state_v6;
 		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) *
@@ -2710,6 +2732,12 @@
 		if (power_state->v2.ucNumDPMLevels) {
 			for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) {
 				clock_array_index = power_state->v2.clockInfoIndex[j];
+				if (clock_array_index >=
+				    clock_info_array->ucNumEntries) {
+					kfree(rdev->pm.power_state[i].clock_info);
+					rdev->pm.power_state[i].clock_info = NULL;
+					goto next_state_v6;
+				}
 				clock_info = (union pplib_clock_info *)
 					&clock_info_array->clockInfo[clock_array_index * clock_info_array->ucEntrySize];
 				valid = radeon_atombios_parse_pplib_clock_info(rdev,
@@ -2731,6 +2759,7 @@
 								   non_clock_info);
 			state_index++;
 		}
+next_state_v6:
 		power_state_offset += 2 + power_state->v2.ucNumDPMLevels;
 	}
 	/* if multiple clock modes, mark the lowest as no display */