DF-1200 / fix.diff
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 */ |