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

Reply via email to