--- a/sys/dev/drm/amd/amdgpu/amdgpu_atombios.c +++ b/sys/dev/drm/amd/amdgpu/amdgpu_atombios.c @@ -1603,6 +1603,7 @@ int amdgpu_atombios_init_mc_reg_table(struct amdgpu_device *adev, union vram_info *vram_info; + u8 *table_end; memset(reg_table, 0, sizeof(struct atom_mc_reg_table)); @@ -1597,9 +1598,14 @@ int amdgpu_atombios_init_mc_reg_table(struct amdgpu_device *adev, vram_info = (union vram_info *) (adev->mode_info.atom_context->bios + data_offset); + /* DF-4154: all walks must stay inside the declared table */ + table_end = (u8 *)vram_info + size; switch (frev) { @@ -1607,6 +1613,10 @@ int amdgpu_atombios_init_mc_reg_table(struct amdgpu_device *adev, if (module_index < vram_info->v2_1.ucNumOfVRAMModule) { ATOM_INIT_REG_BLOCK *reg_block = (ATOM_INIT_REG_BLOCK *) ((u8 *)vram_info + le16_to_cpu(vram_info->v2_1.usMemClkPatchTblOffset)); + if ((u8 *)reg_block + sizeof(*reg_block) > table_end) + return -EINVAL; ATOM_MEMORY_SETTING_DATA_BLOCK *reg_data = (ATOM_MEMORY_SETTING_DATA_BLOCK *) ((u8 *)reg_block + (2 * sizeof(u16)) + le16_to_cpu(reg_block->usRegIndexTblSize)); + if ((u8 *)reg_data + sizeof(u32) > table_end) + return -EINVAL; @@ -1631,12 +1641,18 @@ int amdgpu_atombios_init_mc_reg_table(struct amdgpu_device *adev, reg_table->last = i; + /* DF-4154: zero stride would never terminate */ + if (le16_to_cpu(reg_block->usRegDataBlkSize) == 0) + return -EINVAL; while ((le32_to_cpu(*(u32 *)reg_data) != END_OF_REG_DATA_BLOCK) && (num_ranges < VBIOS_MAX_AC_TIMING_ENTRIES)) { t_mem_id = (u8)((le32_to_cpu(*(u32 *)reg_data) & MEM_ID_MASK) >> MEM_ID_SHIFT); if (module_index == t_mem_id) { + if ((u8 *)reg_data + + 4 + reg_table->last * sizeof(u32) > table_end) + return -EINVAL; reg_table->mc_reg_table_entry[num_ranges].mclk_max = (u32)((le32_to_cpu(*(u32 *)reg_data) & CLOCK_RANGE_MASK) >> CLOCK_RANGE_SHIFT); @@ -1651,6 +1667,9 @@ int amdgpu_atombios_init_mc_reg_table(struct amdgpu_device *adev, } reg_data = (ATOM_MEMORY_SETTING_DATA_BLOCK *) ((u8 *)reg_data + le16_to_cpu(reg_block->usRegDataBlkSize)); + if ((u8 *)reg_data + sizeof(u32) > table_end) + return -EINVAL; }