Reject AC timing blocks with a stride smaller than a dword before walking VBIOS data. A zero stride can otherwise keep reg_data pinned on a nonmatching MEM_ID forever.
Also bound the data-block and END marker reads by the returned VRAM_Info table size so malformed index/data sizes do not push the timing walk past the table. Signed-off-by: Xiang Liu <[email protected]> --- .../drm/amd/pm/powerplay/hwmgr/ppatomctrl.c | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c index ce166a7f8e420..85bf1527c6f44 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c @@ -46,16 +46,22 @@ union voltage_object_info { static int atomctrl_retrieve_ac_timing( uint8_t index, ATOM_INIT_REG_BLOCK *reg_block, + u8 *table_end, pp_atomctrl_mc_reg_table *table) { uint32_t i, j; + u16 stride = le16_to_cpu(reg_block->usRegDataBlkSize); uint8_t tmem_id; ATOM_MEMORY_SETTING_DATA_BLOCK *reg_data = (ATOM_MEMORY_SETTING_DATA_BLOCK *) ((uint8_t *)reg_block + (2 * sizeof(uint16_t)) + le16_to_cpu(reg_block->usRegIndexTblSize)); uint8_t num_ranges = 0; - while (*(uint32_t *)reg_data != END_OF_REG_DATA_BLOCK && + if (stride < sizeof(uint32_t)) + return -EINVAL; + + while ((uint8_t *)reg_data + sizeof(uint32_t) <= table_end && + *(uint32_t *)reg_data != END_OF_REG_DATA_BLOCK && num_ranges < VBIOS_MAX_AC_TIMING_ENTRIES) { tmem_id = (uint8_t)((*(uint32_t *)reg_data & MEM_ID_MASK) >> MEM_ID_SHIFT); @@ -67,6 +73,10 @@ static int atomctrl_retrieve_ac_timing( for (i = 0, j = 1; i < table->last; i++) { if ((table->mc_reg_address[i].uc_pre_reg_data & LOW_NIBBLE_MASK) == DATA_FROM_TABLE) { + if ((uint8_t *)reg_data + + (j + 1) * sizeof(uint32_t) > table_end) + return -EINVAL; + table->mc_reg_table_entry[num_ranges].mc_data[i] = (uint32_t)*((uint32_t *)reg_data + j); j++; @@ -81,11 +91,13 @@ static int atomctrl_retrieve_ac_timing( } reg_data = (ATOM_MEMORY_SETTING_DATA_BLOCK *) - ((uint8_t *)reg_data + le16_to_cpu(reg_block->usRegDataBlkSize)) ; + ((uint8_t *)reg_data + stride); } - PP_ASSERT_WITH_CODE((*(uint32_t *)reg_data == END_OF_REG_DATA_BLOCK), - "Invalid VramInfo table.", return -1); + if ((uint8_t *)reg_data + sizeof(uint32_t) > table_end || + *(uint32_t *)reg_data != END_OF_REG_DATA_BLOCK) + return -EINVAL; + table->num_entries = num_ranges; return 0; @@ -136,6 +148,7 @@ int atomctrl_initialize_mc_reg_table( { ATOM_VRAM_INFO_HEADER_V2_1 *vram_info; ATOM_INIT_REG_BLOCK *reg_block; + u8 *table_end; int result = 0; u8 frev, crev; u16 size; @@ -157,6 +170,7 @@ int atomctrl_initialize_mc_reg_table( } if (0 == result) { + table_end = (uint8_t *)vram_info + size; reg_block = (ATOM_INIT_REG_BLOCK *) ((uint8_t *)vram_info + le16_to_cpu(vram_info->usMemClkPatchTblOffset)); result = atomctrl_set_mc_reg_address_table(reg_block, table); @@ -164,7 +178,7 @@ int atomctrl_initialize_mc_reg_table( if (0 == result) { result = atomctrl_retrieve_ac_timing(module_index, - reg_block, table); + reg_block, table_end, table); } return result; @@ -177,6 +191,7 @@ int atomctrl_initialize_mc_reg_table_v2_2( { ATOM_VRAM_INFO_HEADER_V2_2 *vram_info; ATOM_INIT_REG_BLOCK *reg_block; + u8 *table_end; int result = 0; u8 frev, crev; u16 size; @@ -198,6 +213,7 @@ int atomctrl_initialize_mc_reg_table_v2_2( } if (0 == result) { + table_end = (uint8_t *)vram_info + size; reg_block = (ATOM_INIT_REG_BLOCK *) ((uint8_t *)vram_info + le16_to_cpu(vram_info->usMemClkPatchTblOffset)); result = atomctrl_set_mc_reg_address_table(reg_block, table); @@ -205,7 +221,7 @@ int atomctrl_initialize_mc_reg_table_v2_2( if (0 == result) { result = atomctrl_retrieve_ac_timing(module_index, - reg_block, table); + reg_block, table_end, table); } return result; -- 2.34.1
