Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e4da9d74 authored by Dhaval Patel's avatar Dhaval Patel
Browse files

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: default avatarDhaval Patel <pdhaval@codeaurora.org>
parent 11f73a0c
Loading
Loading
Loading
Loading
+64 −26
Original line number Diff line number Diff line
@@ -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
@@ -569,8 +569,16 @@ static struct sde_prop_type inline_rot_prop[INLINE_ROT_PROP_MAX] = {
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);
@@ -592,6 +600,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]);
@@ -626,8 +642,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;
			}
@@ -670,8 +686,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 &&
@@ -689,14 +706,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;
@@ -732,8 +751,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)
@@ -743,8 +763,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;
@@ -753,8 +774,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 {
@@ -776,19 +798,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,
@@ -1125,6 +1149,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;

@@ -1508,6 +1539,13 @@ static int sde_wb_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg)
			PROP_VALUE_ACCESS(prop_value, WB_ID, i);
		wb->xin_id = PROP_VALUE_ACCESS(prop_value, WB_XIN_ID, i);

		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;
		}

		if (IS_SDE_MAJOR_MINOR_SAME((sde_cfg->hwversion),
				SDE_HW_VER_170))
			wb->vbif_idx = VBIF_NRT;
@@ -2512,7 +2550,7 @@ static int sde_perf_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg)
		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;
@@ -2678,7 +2716,7 @@ static int sde_perf_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg)
			sizeof(struct sde_qos_lut_entry), GFP_KERNEL);
		if (!cfg->perf.qos_lut_tbl[j].entries) {
			rc = -ENOMEM;
			goto end;
			goto freeprop;
		}

		for (k = 0, m = 0; k < count; k++, m += entry_size) {