summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAjay Singh Parmar <aparmar@codeaurora.org>2013-10-29 13:31:36 -0700
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-23 20:23:07 -0700
commit42c53b18dd85fe3251aabb836958befa8c59b953 (patch)
tree51f9796a2d79dea915e4f441a4dc11f982080170
parent32d59d1eaab6d12e189ca16a172041fb382b6d44 (diff)
msm: mdss: hdmi: Proper length check for EDID data block
Check for the invalid length of EDID (Extended Display Identification Data) data blocks while parsing the blocks. For a valid data block, length should not be zero or should not exceed max size. Change-Id: I4e83b07689d2c3161048efabbad35a0ad00e371a Signed-off-by: Ajay Singh Parmar <aparmar@codeaurora.org>
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_edid.c86
1 files changed, 66 insertions, 20 deletions
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
index d4f1b02131e9..39649404574d 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
@@ -16,6 +16,13 @@
#include "mdss_hdmi_edid.h"
#define DBC_START_OFFSET 4
+
+/*
+ * As per CEA-861-E specification 7.5.2, there can be
+ * upto 31 bytes following any tag (data block type).
+ */
+#define MAX_DATA_BLOCK_SIZE 31
+
#define HDMI_VSDB_3D_EVF_DATA_OFFSET(vsd) \
(!((vsd)[8] & BIT(7)) ? 9 : (!((vsd)[8] & BIT(6)) ? 11 : 13))
@@ -32,6 +39,17 @@
#define BUFF_SIZE_3D 128
+enum data_block_types {
+ RESERVED,
+ AUDIO_DATA_BLOCK,
+ VIDEO_DATA_BLOCK,
+ VENDOR_SPECIFIC_DATA_BLOCK,
+ SPEAKER_ALLOCATION_DATA_BLOCK,
+ VESA_DTC_DATA_BLOCK,
+ RESERVED2,
+ USE_EXTENDED_TAG
+};
+
struct hdmi_edid_sink_data {
u32 disp_mode_list[HDMI_VFRMT_MAX];
u32 disp_3d_mode_list[HDMI_VFRMT_MAX];
@@ -526,7 +544,8 @@ static void hdmi_edid_extract_extended_data_blocks(
}
/* A Tage code of 7 identifies extended data blocks */
- etag = hdmi_edid_find_block(in_buf, start_offset, 7, &len);
+ etag = hdmi_edid_find_block(in_buf, start_offset, USE_EXTENDED_TAG,
+ &len);
while (etag != NULL) {
/* The extended data block should at least be 2 bytes long */
@@ -572,7 +591,8 @@ static void hdmi_edid_extract_extended_data_blocks(
/* There could be more that one extended data block */
start_offset = etag - in_buf + len + 1;
- etag = hdmi_edid_find_block(in_buf, start_offset, 7, &len);
+ etag = hdmi_edid_find_block(in_buf, start_offset,
+ USE_EXTENDED_TAG, &len);
}
} /* hdmi_edid_extract_extended_data_blocks */
@@ -587,11 +607,12 @@ static void hdmi_edid_extract_3d_present(struct hdmi_edid_ctrl *edid_ctrl,
return;
}
- vsd = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 3, &len);
+ vsd = hdmi_edid_find_block(in_buf, DBC_START_OFFSET,
+ VENDOR_SPECIFIC_DATA_BLOCK, &len);
edid_ctrl->present_3d = 0;
- if (vsd == NULL || len < 9) {
- DEV_DBG("%s: blk-id 3 not found or not long enough\n",
+ if (vsd == NULL || len == 0 || len > MAX_DATA_BLOCK_SIZE) {
+ DEV_DBG("%s: No/Invalid vendor Specific Data Block\n",
__func__);
return;
}
@@ -618,9 +639,13 @@ static void hdmi_edid_extract_audio_data_blocks(
return;
}
- adb = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 1, &len);
- if ((adb == NULL) || (len > MAX_AUDIO_DATA_BLOCK_SIZE))
+ adb = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, AUDIO_DATA_BLOCK,
+ &len);
+ if ((adb == NULL) || (len > MAX_AUDIO_DATA_BLOCK_SIZE)) {
+ DEV_DBG("%s: No/Invalid Audio Data Block\n",
+ __func__);
return;
+ }
memcpy(edid_ctrl->audio_data_block, adb + 1, len);
edid_ctrl->adb_size = len;
@@ -646,9 +671,13 @@ static void hdmi_edid_extract_speaker_allocation_data(
return;
}
- sadb = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 4, &len);
- if ((sadb == NULL) || (len != MAX_SPKR_ALLOC_DATA_BLOCK_SIZE))
+ sadb = hdmi_edid_find_block(in_buf, DBC_START_OFFSET,
+ SPEAKER_ALLOCATION_DATA_BLOCK, &len);
+ if ((sadb == NULL) || (len != MAX_SPKR_ALLOC_DATA_BLOCK_SIZE)) {
+ DEV_DBG("%s: No/Invalid Speaker Allocation Data Block\n",
+ __func__);
return;
+ }
memcpy(edid_ctrl->spkr_alloc_data_block, sadb + 1, len);
edid_ctrl->sadb_size = len;
@@ -675,9 +704,11 @@ static void hdmi_edid_extract_latency_fields(struct hdmi_edid_ctrl *edid_ctrl,
return;
}
- vsd = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 3, &len);
+ vsd = hdmi_edid_find_block(in_buf, DBC_START_OFFSET,
+ VENDOR_SPECIFIC_DATA_BLOCK, &len);
- if (vsd == NULL || len < 12 || !(vsd[8] & BIT(7))) {
+ if (vsd == NULL || len == 0 || len > MAX_DATA_BLOCK_SIZE ||
+ !(vsd[8] & BIT(7))) {
edid_ctrl->video_latency = (u16)-1;
edid_ctrl->audio_latency = (u16)-1;
DEV_DBG("%s: EDID: No audio/video latency present\n", __func__);
@@ -701,9 +732,14 @@ static u32 hdmi_edid_extract_ieee_reg_id(struct hdmi_edid_ctrl *edid_ctrl,
return 0;
}
- vsd = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 3, &len);
- if (vsd == NULL || len < 8)
+ vsd = hdmi_edid_find_block(in_buf, DBC_START_OFFSET,
+ VENDOR_SPECIFIC_DATA_BLOCK, &len);
+
+ if (vsd == NULL || len == 0 || len > MAX_DATA_BLOCK_SIZE) {
+ DEV_DBG("%s: No/Invalid Vendor Specific Data Block\n",
+ __func__);
return 0;
+ }
DEV_DBG("%s: EDID: VSD PhyAddr=%04x, MaxTMDS=%dMHz\n", __func__,
((u32)vsd[4] << 8) + (u32)vsd[5], (u32)vsd[7] * 5);
@@ -900,11 +936,14 @@ static int hdmi_edid_get_display_vsd_3d_mode(const u8 *data_buf,
u16 structure_all, structure_mask;
const u8 *vsd = num_of_cea_blocks ?
hdmi_edid_find_block(data_buf+0x80, DBC_START_OFFSET,
- 3, &len) : NULL;
+ VENDOR_SPECIFIC_DATA_BLOCK, &len) : NULL;
int i;
- if (!vsd)
+ if (vsd == NULL || len == 0 || len > MAX_DATA_BLOCK_SIZE) {
+ DEV_DBG("%s: No/Invalid Vendor Specific Data Block\n",
+ __func__);
return -ENXIO;
+ }
offset = HDMI_VSDB_3D_EVF_DATA_OFFSET(vsd);
if (offset >= len - 1)
@@ -1046,10 +1085,11 @@ static void hdmi_edid_get_extended_video_formats(
return;
}
- vsd = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 3, &db_len);
+ vsd = hdmi_edid_find_block(in_buf, DBC_START_OFFSET,
+ VENDOR_SPECIFIC_DATA_BLOCK, &db_len);
- if (vsd == NULL || db_len < 9) {
- DEV_DBG("%s: blk-id 3 not found or not long enough\n",
+ if (vsd == NULL || db_len == 0 || db_len > MAX_DATA_BLOCK_SIZE) {
+ DEV_DBG("%s: No/Invalid Vendor Specific Data Block\n",
__func__);
return;
}
@@ -1099,8 +1139,14 @@ static void hdmi_edid_get_display_mode(struct hdmi_edid_ctrl *edid_ctrl,
edid_blk0 = &data_buf[0x0];
edid_blk1 = &data_buf[0x80];
svd = num_of_cea_blocks ?
- hdmi_edid_find_block(data_buf+0x80, DBC_START_OFFSET, 2,
- &len) : NULL;
+ hdmi_edid_find_block(data_buf+0x80, DBC_START_OFFSET,
+ VIDEO_DATA_BLOCK, &len) : NULL;
+
+ if (svd == NULL || len == 0 || len > MAX_DATA_BLOCK_SIZE) {
+ DEV_DBG("%s: No/Invalid Video Data Block\n",
+ __func__);
+ return;
+ }
sink_data = &edid_ctrl->sink_data;