Commit 67bc09daa8fc ("drm/amd/display: Parse all extension blocks for VSDB")
changed the extension block parser from going over just one block to
reading up to as many blocks as specified in edid->extensions.

This is quite obviously broken because the loop directly above goes over
the extension blocks already and filters for the first DisplayID
extension block, so edid_ext might point to any random block, perhaps
even the very last block. Reading edid->extensions * EDID_LENGTH bytes
from here will almost certainly go out-of-bounds.

I suspect the intention was instead to look at all DisplayID blocks in
the EDID, which is what this patch implements.

This is not tagged with Cc: stable because AFAICS the offending commit
didn't make it to any stable PR yet.

Fixes: 67bc09daa8fc ("drm/amd/display: Parse all extension blocks for VSDB")

Signed-off-by: Natalie Vock <[email protected]>
---
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 24 ++++++++++---------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 50a10b4fbb3ff..128a262068ec2 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -13093,7 +13093,6 @@ static int parse_amd_vsdb(struct amdgpu_dm_connector 
*aconnector,
        u8 *edid_ext = NULL;
        int i;
        int j = 0;
-       int total_ext_block_len;
 
        if (edid == NULL || edid->extensions == 0)
                return -ENODEV;
@@ -13101,17 +13100,21 @@ static int parse_amd_vsdb(struct amdgpu_dm_connector 
*aconnector,
        /* Find DisplayID extension */
        for (i = 0; i < edid->extensions; i++) {
                edid_ext = (void *)(edid + (i + 1));
-               if (edid_ext[0] == DISPLAYID_EXT)
-                       break;
-       }
+               if (edid_ext[0] != DISPLAYID_EXT)
+                       continue;
 
-       total_ext_block_len = EDID_LENGTH * edid->extensions;
-       while (j < total_ext_block_len - sizeof(struct amd_vsdb_block)) {
-               struct amd_vsdb_block *amd_vsdb = (struct amd_vsdb_block 
*)&edid_ext[j];
-               unsigned int ieeeId = (amd_vsdb->ieee_id[2] << 16) | 
(amd_vsdb->ieee_id[1] << 8) | (amd_vsdb->ieee_id[0]);
+               j = 0;
+               while (j < EDID_LENGTH - sizeof(struct amd_vsdb_block)) {
+                       struct amd_vsdb_block *amd_vsdb = (struct 
amd_vsdb_block *)&edid_ext[j];
+                       unsigned int ieeeId = (amd_vsdb->ieee_id[2] << 16) | 
(amd_vsdb->ieee_id[1] << 8) |
+                               (amd_vsdb->ieee_id[0]);
+
+                       if (ieeeId != 
HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_IEEE_REGISTRATION_ID ||
+                                       amd_vsdb->version != 
HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_VERSION_3) {
+                               j++;
+                               continue;
+                       }
 
-               if (ieeeId == 
HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_IEEE_REGISTRATION_ID &&
-                               amd_vsdb->version == 
HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_VERSION_3) {
                        u8 panel_type;
                        vsdb_info->replay_mode = (amd_vsdb->feature_caps & 
AMD_VSDB_VERSION_3_FEATURECAP_REPLAYMODE) ? true : false;
                        vsdb_info->amd_vsdb_version = 
HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_VERSION_3;
@@ -13133,7 +13136,6 @@ static int parse_amd_vsdb(struct amdgpu_dm_connector 
*aconnector,
 
                        return true;
                }
-               j++;
        }
 
        return false;
-- 
2.53.0

Reply via email to