From 0667b6f91681357b59952daceac911b9f89eb634 Mon Sep 17 00:00:00 2001 From: Dhaval Patel Date: Mon, 19 Jun 2017 16:51:21 -0700 Subject: [PATCH] drm/msm/sde: remove out of bound access for qos lut parsing QOS LUT dtsi entries use existing hardware parsing APIs but dos not increase the size of array. This causes out of bound access while reading u32 lut array entry. This patch fixes the array size and also adds checks to avoid future out of bound access. It also fixes the memory leak in qos lut parsing. Change-Id: I98de052d03e1bcfd79d15ab99ca41d7782e56682 Signed-off-by: Dhaval Patel Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/sde/sde_hw_catalog.c | 89 +++++++++++++++++------- 1 file changed, 64 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c index 1a563ea5f50c..ed9a6ea37397 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c @@ -27,11 +27,11 @@ /** * Max hardware block in certain hardware. For ex: sspp pipes - * can have QSEED, pcc, igc, pa, csc, etc. This count is max - * 12 based on software design. It should be increased if any of the + * can have QSEED, pcc, igc, pa, csc, qos entries, etc. This count is + * 64 based on software design. It should be increased if any of the * hardware block has more subblocks. */ -#define MAX_SDE_HW_BLK 12 +#define MAX_SDE_HW_BLK 64 /* each entry will have register address and bit offset in that register */ #define MAX_BIT_OFFSET 2 @@ -444,8 +444,16 @@ static uint32_t _sde_copy_formats( static int _parse_dt_u32_handler(struct device_node *np, char *prop_name, u32 *offsets, int len, bool mandatory) { - int rc = of_property_read_u32_array(np, prop_name, offsets, len); + int rc = -EINVAL; + if (len > MAX_SDE_HW_BLK) { + SDE_ERROR( + "prop: %s tries out of bound access for u32 array read len: %d\n", + prop_name, len); + return -E2BIG; + } + + rc = of_property_read_u32_array(np, prop_name, offsets, len); if (rc && mandatory) SDE_ERROR("mandatory prop: %s u32 array read len:%d\n", prop_name, len); @@ -467,6 +475,14 @@ static int _parse_dt_bit_offset(struct device_node *np, if (arr) { len /= sizeof(u32); len &= ~0x1; + + if (len > (MAX_SDE_HW_BLK * MAX_BIT_OFFSET)) { + SDE_ERROR( + "prop: %s len: %d will lead to out of bound access\n", + prop_name, len / MAX_BIT_OFFSET); + return -E2BIG; + } + for (i = 0, j = 0; i < len; j++) { PROP_BITVALUE_ACCESS(prop_value, prop_index, j, 0) = be32_to_cpu(arr[i]); @@ -501,8 +517,8 @@ static int _validate_dt_entry(struct device_node *np, sde_prop[0].prop_name); if ((*off_count > MAX_BLOCKS) || (*off_count < 0)) { if (sde_prop[0].is_mandatory) { - SDE_ERROR("invalid hw offset prop name:%s\"\ - count: %d\n", + SDE_ERROR( + "invalid hw offset prop name:%s count: %d\n", sde_prop[0].prop_name, *off_count); rc = -EINVAL; } @@ -545,8 +561,9 @@ static int _validate_dt_entry(struct device_node *np, sde_prop[i].type); break; } - SDE_DEBUG("prop id:%d prop name:%s prop type:%d \"\ - prop_count:%d\n", i, sde_prop[i].prop_name, + SDE_DEBUG( + "prop id:%d prop name:%s prop type:%d prop_count:%d\n", + i, sde_prop[i].prop_name, sde_prop[i].type, prop_count[i]); if (rc && sde_prop[i].is_mandatory && @@ -564,14 +581,16 @@ static int _validate_dt_entry(struct device_node *np, if (off_count && (prop_count[i] != *off_count) && sde_prop[i].is_mandatory) { - SDE_ERROR("prop:%s count:%d is different compared to \"\ - offset array:%d\n", sde_prop[i].prop_name, + SDE_ERROR( + "prop:%s count:%d is different compared to offset array:%d\n", + sde_prop[i].prop_name, prop_count[i], *off_count); rc = -EINVAL; goto end; } else if (off_count && prop_count[i] != *off_count) { - SDE_DEBUG("prop:%s count:%d is different compared to \"\ - offset array:%d\n", sde_prop[i].prop_name, + SDE_DEBUG( + "prop:%s count:%d is different compared to offset array:%d\n", + sde_prop[i].prop_name, prop_count[i], *off_count); rc = 0; prop_count[i] = 0; @@ -607,8 +626,9 @@ static int _read_dt_entry(struct device_node *np, case PROP_TYPE_U32: rc = of_property_read_u32(np, sde_prop[i].prop_name, &PROP_VALUE_ACCESS(prop_value, i, 0)); - SDE_DEBUG("prop id:%d prop name:%s prop type:%d \"\ - value:0x%x\n", i, sde_prop[i].prop_name, + SDE_DEBUG( + "prop id:%d prop name:%s prop type:%d value:0x%x\n", + i, sde_prop[i].prop_name, sde_prop[i].type, PROP_VALUE_ACCESS(prop_value, i, 0)); if (rc) @@ -618,8 +638,9 @@ static int _read_dt_entry(struct device_node *np, PROP_VALUE_ACCESS(prop_value, i, 0) = of_property_read_bool(np, sde_prop[i].prop_name); - SDE_DEBUG("prop id:%d prop name:%s prop type:%d \"\ - value:0x%x\n", i, sde_prop[i].prop_name, + SDE_DEBUG( + "prop id:%d prop name:%s prop type:%d value:0x%x\n", + i, sde_prop[i].prop_name, sde_prop[i].type, PROP_VALUE_ACCESS(prop_value, i, 0)); break; @@ -628,8 +649,9 @@ static int _read_dt_entry(struct device_node *np, &PROP_VALUE_ACCESS(prop_value, i, 0), prop_count[i], sde_prop[i].is_mandatory); if (rc && sde_prop[i].is_mandatory) { - SDE_ERROR("%s prop validation success but \"\ - read failed\n", sde_prop[i].prop_name); + SDE_ERROR( + "%s prop validation success but read failed\n", + sde_prop[i].prop_name); prop_exists[i] = false; goto end; } else { @@ -651,19 +673,21 @@ static int _read_dt_entry(struct device_node *np, prop_value, i, prop_count[i], sde_prop[i].is_mandatory); if (rc && sde_prop[i].is_mandatory) { - SDE_ERROR("%s prop validation success but \"\ - read failed\n", sde_prop[i].prop_name); + SDE_ERROR( + "%s prop validation success but read failed\n", + sde_prop[i].prop_name); prop_exists[i] = false; goto end; } else { if (rc) prop_exists[i] = false; - SDE_DEBUG("prop id:%d prop name:%s prop \"\ - type:%d", i, sde_prop[i].prop_name, + SDE_DEBUG( + "prop id:%d prop name:%s prop type:%d", + i, sde_prop[i].prop_name, sde_prop[i].type); for (j = 0; j < prop_count[i]; j++) - SDE_DEBUG(" count[%d]: bit:0x%x \"\ - off:0x%x \n", j, + SDE_DEBUG( + "count[%d]: bit:0x%x off:0x%x\n", j, PROP_BITVALUE_ACCESS(prop_value, i, j, 0), PROP_BITVALUE_ACCESS(prop_value, @@ -984,6 +1008,13 @@ static int sde_sspp_parse_dt(struct device_node *np, snprintf(sblk->src_blk.name, SDE_HW_BLK_NAME_LEN, "sspp_src_%u", sspp->id - SSPP_VIG0); + if (sspp->clk_ctrl >= SDE_CLK_CTRL_MAX) { + SDE_ERROR("%s: invalid clk ctrl: %d\n", + sblk->src_blk.name, sspp->clk_ctrl); + rc = -EINVAL; + goto end; + } + sblk->maxhdeciexp = MAX_HORZ_DECIMATION; sblk->maxvdeciexp = MAX_VERT_DECIMATION; @@ -1345,6 +1376,14 @@ static int sde_wb_parse_dt(struct device_node *np, PROP_VALUE_ACCESS(prop_value, WB_ID, i); wb->xin_id = PROP_VALUE_ACCESS(prop_value, WB_XIN_ID, i); wb->vbif_idx = VBIF_NRT; + + if (wb->clk_ctrl >= SDE_CLK_CTRL_MAX) { + SDE_ERROR("%s: invalid clk ctrl: %d\n", + wb->name, wb->clk_ctrl); + rc = -EINVAL; + goto end; + } + wb->len = PROP_VALUE_ACCESS(prop_value, WB_LEN, 0); wb->format_list = wb2_formats; if (!prop_exists[WB_LEN]) @@ -2067,7 +2106,7 @@ static int sde_perf_parse_dt(struct device_node *np, goto end; } - prop_value = kzalloc(SDE_PROP_MAX * + prop_value = kzalloc(PERF_PROP_MAX * sizeof(struct sde_prop_value), GFP_KERNEL); if (!prop_value) { rc = -ENOMEM;