The Vega hwmgr PowerPlay table parsers read fixed table fields, state array entries, or SMC PPT fields before validating that the VBIOS table buffer covers those structures. A truncated table can therefore lead to out-of-bounds reads during hwmgr initialization.
Reject tables smaller than the fixed PowerPlay table. For Vega10, also validate the state array offset and entry range before dereferencing the state array. Signed-off-by: Yang Wang <[email protected]> --- .../powerplay/hwmgr/vega10_processpptables.c | 59 ++++++++++++------- .../powerplay/hwmgr/vega12_processpptables.c | 7 +++ .../powerplay/hwmgr/vega20_processpptables.c | 7 +++ 3 files changed, 52 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c index 3be616af327e..64cff9f4850a 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c @@ -63,14 +63,17 @@ static const void *get_powerplay_table(struct pp_hwmgr *hwmgr) return table_address; } -static int check_powerplay_tables( - struct pp_hwmgr *hwmgr, - const ATOM_Vega10_POWERPLAYTABLE *powerplay_table) +static int get_vega10_state_array(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + const ATOM_Vega10_State_Array **state_array) { const ATOM_Vega10_State_Array *state_arrays; + u16 state_array_offset; + size_t state_array_size; + size_t table_size = hwmgr->soft_pp_table_size; - state_arrays = (ATOM_Vega10_State_Array *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usStateArrayOffset)); + PP_ASSERT_WITH_CODE((table_size >= sizeof(*powerplay_table)), + "Invalid PowerPlay Table!", return -1); PP_ASSERT_WITH_CODE((powerplay_table->sHeader.format_revision >= ATOM_Vega10_TABLE_REVISION_VEGA10), @@ -79,12 +82,34 @@ static int check_powerplay_tables( "State table is not set!", return -1); PP_ASSERT_WITH_CODE(powerplay_table->sHeader.structuresize > 0, "Invalid PowerPlay Table!", return -1); + + state_array_offset = le16_to_cpu(powerplay_table->usStateArrayOffset); + PP_ASSERT_WITH_CODE((state_array_offset <= + table_size - sizeof(*state_arrays)), + "Invalid PowerPlay Table!", return -1); + + state_arrays = (ATOM_Vega10_State_Array *)(((unsigned long)powerplay_table) + + state_array_offset); PP_ASSERT_WITH_CODE(state_arrays->ucNumEntries > 0, "Invalid PowerPlay Table!", return -1); + state_array_size = struct_size(state_arrays, states, state_arrays->ucNumEntries); + PP_ASSERT_WITH_CODE((state_array_size <= table_size - state_array_offset), + "Invalid PowerPlay Table!", return -1); + + *state_array = state_arrays; + return 0; } +static int check_powerplay_tables(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table) +{ + const ATOM_Vega10_State_Array *state_arrays; + + return get_vega10_state_array(hwmgr, powerplay_table, &state_arrays); +} + static int set_platform_caps(struct pp_hwmgr *hwmgr, uint32_t powerplay_caps) { set_hw_cap( @@ -1249,15 +1274,14 @@ int vega10_get_number_of_powerplay_table_entries(struct pp_hwmgr *hwmgr) { const ATOM_Vega10_State_Array *state_arrays; const ATOM_Vega10_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr); + int result; PP_ASSERT_WITH_CODE((pp_table != NULL), "Missing PowerPlay Table!", return -1); - PP_ASSERT_WITH_CODE((pp_table->sHeader.format_revision >= - ATOM_Vega10_TABLE_REVISION_VEGA10), - "Incorrect PowerPlay table revision!", return -1); - state_arrays = (ATOM_Vega10_State_Array *)(((unsigned long)pp_table) + - le16_to_cpu(pp_table->usStateArrayOffset)); + result = get_vega10_state_array(hwmgr, pp_table, &state_arrays); + PP_ASSERT_WITH_CODE((result == 0), + "Invalid PowerPlay Table State Array.", return result); return (uint32_t)(state_arrays->ucNumEntries); } @@ -1308,17 +1332,11 @@ int vega10_get_powerplay_table_entry(struct pp_hwmgr *hwmgr, if (pp_table->sHeader.format_revision >= ATOM_Vega10_TABLE_REVISION_VEGA10) { - state_arrays = (ATOM_Vega10_State_Array *) - (((unsigned long)pp_table) + - le16_to_cpu(pp_table->usStateArrayOffset)); - - PP_ASSERT_WITH_CODE(pp_table->usStateArrayOffset > 0, - "Invalid PowerPlay Table State Array Offset.", - return -1); - PP_ASSERT_WITH_CODE(state_arrays->ucNumEntries > 0, + result = get_vega10_state_array(hwmgr, pp_table, &state_arrays); + PP_ASSERT_WITH_CODE((result == 0), "Invalid PowerPlay Table State Array.", - return -1); - PP_ASSERT_WITH_CODE((entry_index <= state_arrays->ucNumEntries), + return result); + PP_ASSERT_WITH_CODE((entry_index < state_arrays->ucNumEntries), "Invalid PowerPlay Table State Array Entry.", return -1); @@ -1360,4 +1378,3 @@ int vega10_baco_set_cap(struct pp_hwmgr *hwmgr) PHM_PlatformCaps_BACO); return result; } - diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c index 89148f73b514..8e6b1565d00d 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c @@ -64,6 +64,13 @@ static int check_powerplay_tables( struct pp_hwmgr *hwmgr, const ATOM_Vega12_POWERPLAYTABLE *powerplay_table) { + size_t smc_pptable_size = + offsetofend(ATOM_Vega12_POWERPLAYTABLE, smcPPTable); + size_t table_size = hwmgr->soft_pp_table_size; + + PP_ASSERT_WITH_CODE((table_size >= smc_pptable_size), + "Invalid PowerPlay Table!", return -1); + PP_ASSERT_WITH_CODE((powerplay_table->sHeader.format_revision >= ATOM_VEGA12_TABLE_REVISION_VEGA12), "Unsupported PPTable format!", return -1); diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c index 2b446f8866ba..0d008573c038 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c @@ -66,6 +66,13 @@ static int check_powerplay_tables( struct pp_hwmgr *hwmgr, const ATOM_Vega20_POWERPLAYTABLE *powerplay_table) { + size_t smc_pptable_size = + offsetofend(ATOM_Vega20_POWERPLAYTABLE, smcPPTable); + size_t table_size = hwmgr->soft_pp_table_size; + + PP_ASSERT_WITH_CODE((table_size >= smc_pptable_size), + "Invalid PowerPlay Table!", return -1); + PP_ASSERT_WITH_CODE((powerplay_table->sHeader.format_revision >= ATOM_VEGA20_TABLE_REVISION_VEGA20), "Unsupported PPTable format!", return -1); -- 2.47.3
