process_pptables_v1_0.c builds the Tonga state array pointer
from usStateArrayOffset before checking that the table buffer covers
the referenced data. A truncated PowerPlay table can therefore lead to
out-of-bounds reads while validating the state array.

Validate the fixed table size first, then check the state array offset
and entry range before dereferencing the state array.

Signed-off-by: Yang Wang <[email protected]>
---
 .../powerplay/hwmgr/process_pptables_v1_0.c   | 62 ++++++++++++-------
 1 file changed, 40 insertions(+), 22 deletions(-)

diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c 
b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c
index 6cfef1b295ab..94c1e2c10370 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c
@@ -1113,15 +1113,17 @@ static int init_thermal_controller(
  * @powerplay_table: Pointer to the PowerPlay Table.
  * Exception:  2 if the powerplay table is incorrect.
  */
-static int check_powerplay_tables(
-               struct pp_hwmgr *hwmgr,
-               const ATOM_Tonga_POWERPLAYTABLE *powerplay_table
-               )
+static int get_tonga_state_array(struct pp_hwmgr *hwmgr,
+       const ATOM_Tonga_POWERPLAYTABLE *powerplay_table,
+       const ATOM_Tonga_State_Array **state_array)
 {
        const ATOM_Tonga_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_Tonga_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((ATOM_Tonga_TABLE_REVISION_TONGA <=
                powerplay_table->sHeader.ucTableFormatRevision),
@@ -1130,12 +1132,34 @@ static int check_powerplay_tables(
                "State table is not set!", return -1);
        PP_ASSERT_WITH_CODE((0 < powerplay_table->sHeader.usStructureSize),
                "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_Tonga_State_Array *)(((unsigned 
long)powerplay_table) +
+               state_array_offset);
        PP_ASSERT_WITH_CODE((0 < state_arrays->ucNumEntries),
                "Invalid PowerPlay Table!", return -1);
 
+       state_array_size = struct_size(state_arrays, entries, 
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_Tonga_POWERPLAYTABLE *powerplay_table)
+{
+       const ATOM_Tonga_State_Array *state_arrays;
+
+       return get_tonga_state_array(hwmgr, powerplay_table, &state_arrays);
+}
+
 static int pp_tables_v1_0_initialize(struct pp_hwmgr *hwmgr)
 {
        int result = 0;
@@ -1239,17 +1263,16 @@ const struct pp_table_func pptable_v1_0_funcs = {
 
 int get_number_of_powerplay_table_entries_v1_0(struct pp_hwmgr *hwmgr)
 {
-       ATOM_Tonga_State_Array const *state_arrays;
+       const ATOM_Tonga_State_Array *state_arrays;
        const ATOM_Tonga_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr);
+       int result;
 
        PP_ASSERT_WITH_CODE((NULL != pp_table),
                        "Missing PowerPlay Table!", return -1);
-       PP_ASSERT_WITH_CODE((pp_table->sHeader.ucTableFormatRevision >=
-                       ATOM_Tonga_TABLE_REVISION_TONGA),
-                       "Incorrect PowerPlay table revision!", return -1);
 
-       state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)pp_table) +
-                       le16_to_cpu(pp_table->usStateArrayOffset));
+       result = get_tonga_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);
 }
@@ -1380,15 +1403,11 @@ int get_powerplay_table_entry_v1_0(struct pp_hwmgr 
*hwmgr,
 
        if (pp_table->sHeader.ucTableFormatRevision >=
                        ATOM_Tonga_TABLE_REVISION_TONGA) {
-               state_arrays = (ATOM_Tonga_State_Array *)(((unsigned 
long)pp_table) +
-                               le16_to_cpu(pp_table->usStateArrayOffset));
-
-               PP_ASSERT_WITH_CODE((0 < pp_table->usStateArrayOffset),
-                               "Invalid PowerPlay Table State Array Offset.", 
return -1);
-               PP_ASSERT_WITH_CODE((0 < state_arrays->ucNumEntries),
-                               "Invalid PowerPlay Table State Array.", return 
-1);
-               PP_ASSERT_WITH_CODE((entry_index <= state_arrays->ucNumEntries),
-                               "Invalid PowerPlay Table State Array Entry.", 
return -1);
+               result = get_tonga_state_array(hwmgr, pp_table, &state_arrays);
+               PP_ASSERT_WITH_CODE((result == 0),
+                                   "Invalid PowerPlay Table State Array.", 
return result);
+               PP_ASSERT_WITH_CODE((entry_index < state_arrays->ucNumEntries),
+                                   "Invalid PowerPlay Table State Array 
Entry.", return -1);
 
                state_entry = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
                                                ATOM_Tonga_State, entries,
@@ -1414,4 +1433,3 @@ int get_powerplay_table_entry_v1_0(struct pp_hwmgr *hwmgr,
 
        return result;
 }
-
-- 
2.47.3

Reply via email to