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

Reply via email to