Prechádzať zdrojové kódy

Merge "msm: camera: cdm: Prevention from OOB on virtual cdm write" into camera-kernel.lnx.7.0

Camera Software Integration 1 rok pred
rodič
commit
1aa641e0f3
1 zmenil súbory, kde vykonal 201 pridanie a 62 odobranie
  1. 201 62
      drivers/cam_cdm/cam_cdm_util.c

+ 201 - 62
drivers/cam_cdm/cam_cdm_util.c

@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
- * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include <linux/types.h>
@@ -489,115 +489,253 @@ int cam_cdm_get_ioremap_from_base(uint32_t hw_base,
 	return ret;
 }
 
-static int cam_cdm_util_reg_cont_write(void __iomem *base_addr,
-	uint32_t *cmd_buf, uint32_t cmd_buf_size, uint32_t *used_bytes)
+static int cam_cdm_util_cmd_buf_validation(void __iomem *base_addr,
+	uint32_t base_array_size,
+	struct cam_soc_reg_map *base_table[CAM_SOC_MAX_BLOCK],
+	uint32_t cmd_buf_size, uint32_t *cmd_buf, void *buf,
+	resource_size_t *size,
+	enum cam_cdm_command cmd_type)
 {
-	int ret = 0;
-	uint32_t *data;
-	struct cdm_regcontinuous_cmd *reg_cont;
 
-	if ((cmd_buf_size < cam_cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT)) ||
-		(!base_addr)) {
-		CAM_ERR(CAM_CDM, "invalid base addr and data length  %d %pK",
-			cmd_buf_size, base_addr);
+	int i, ret = 0;
+
+	if (!base_addr) {
+		CAM_ERR(CAM_CDM, "invalid base address");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < base_array_size; i++) {
+		if ((base_table[i]) &&
+			((base_table[i])->mem_base == base_addr)) {
+			*size = (base_table[i])->size;
+			break;
+		}
+	}
+
+	if (*size == 0) {
+		CAM_ERR(CAM_CDM, "Could not retrieve ioremap size, address not mapped!");
 		return -EINVAL;
 	}
 
-	reg_cont = (struct cdm_regcontinuous_cmd *)cmd_buf;
-	if ((!reg_cont->count) || (((reg_cont->count * sizeof(uint32_t)) +
+	switch (cmd_type) {
+	case CAM_CDM_CMD_REG_RANDOM: {
+		struct cdm_regrandom_cmd *reg_random = (struct cdm_regrandom_cmd *)buf;
+		uint32_t *data, offset;
+
+		if ((!reg_random->count) || (((reg_random->count * (sizeof(uint32_t) * 2)) +
+			cam_cdm_get_cmd_header_size(CAM_CDM_CMD_REG_RANDOM)) >
+				cmd_buf_size)) {
+			CAM_ERR(CAM_CDM, "invalid reg_count  %d cmd_buf_size %d",
+				reg_random->count, cmd_buf_size);
+			ret = -EINVAL;
+		}
+
+		data = cmd_buf + cam_cdm_get_cmd_header_size(CAM_CDM_CMD_REG_RANDOM);
+
+		for (i = 0; i < reg_random->count; i++) {
+			offset = data[0];
+			if (offset > *size) {
+				CAM_ERR(CAM_CDM, "Offset out of mapped range! size:%llu offset:%u",
+					*size, offset);
+				return -EINVAL;
+			}
+			data += 2;
+		}
+		}
+		break;
+	case CAM_CDM_CMD_REG_CONT: {
+		struct cdm_regcontinuous_cmd *reg_cont = (struct cdm_regcontinuous_cmd *) buf;
+
+		if ((!reg_cont->count) || (((reg_cont->count * sizeof(uint32_t)) +
 			cam_cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT)) >
 			cmd_buf_size)) {
-		CAM_ERR(CAM_CDM, "buffer size %d is not sufficient for count%d",
-			cmd_buf_size, reg_cont->count);
-		return -EINVAL;
+			CAM_ERR(CAM_CDM, "buffer size %d is not sufficient for count%d",
+				cmd_buf_size, reg_cont->count);
+			ret = -EINVAL;
+		}
+
+		if ((reg_cont->offset > *size) && ((reg_cont->offset +
+			(reg_cont->count * sizeof(uint32_t))) > *size)) {
+			CAM_ERR(CAM_CDM, "Offset out of mapped range! size: %lu, offset: %u",
+				*size, reg_cont->offset);
+			return -EINVAL;
+		}
+		}
+		break;
+	case CAM_CDM_CMD_SWD_DMI_64: {
+		struct cdm_dmi_cmd *swd_dmi = (struct cdm_dmi_cmd *) buf;
+
+		if (cmd_buf_size < (cam_cdm_required_size_dmi() + swd_dmi->length + 1)) {
+			CAM_ERR(CAM_CDM, "invalid CDM_SWD_DMI length %d",
+				swd_dmi->length + 1);
+			ret = -EINVAL;
+		}
+
+		if ((swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_LO_OFFSET > *size) ||
+			(swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_HI_OFFSET > *size)) {
+			CAM_ERR(CAM_CDM,
+				"Offset out of mapped range! size:%llu lo_offset:%u hi_offset:%u",
+				*size, swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_LO_OFFSET,
+				swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_LO_OFFSET);
+			return -EINVAL;
+		}
+		}
+		break;
+	case CAM_CDM_CMD_SWD_DMI_32: {
+		struct cdm_dmi_cmd *swd_dmi = (struct cdm_dmi_cmd *) buf;
+
+		if (cmd_buf_size < (cam_cdm_required_size_dmi() + swd_dmi->length + 1)) {
+			CAM_ERR(CAM_CDM, "invalid CDM_SWD_DMI length %d",
+				swd_dmi->length + 1);
+			ret = -EINVAL;
+		}
+
+		if (swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_LO_OFFSET > *size) {
+			CAM_ERR(CAM_CDM,
+				"Offset out of mapped range! size:%llu lo_offset:%u",
+				*size, swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_LO_OFFSET);
+			return -EINVAL;
+		}
+		}
+		break;
+	case CAM_CDM_CMD_DMI: {
+		struct cdm_dmi_cmd *swd_dmi = (struct cdm_dmi_cmd *) buf;
+
+		if (cmd_buf_size < (cam_cdm_required_size_dmi() + swd_dmi->length + 1)) {
+			CAM_ERR(CAM_CDM, "invalid CDM_SWD_DMI length %d",
+				swd_dmi->length + 1);
+			ret = -EINVAL;
+		}
+
+		if (swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_OFFSET > *size) {
+			CAM_ERR(CAM_CDM,
+				"Offset out of mapped range! size:%llu offset:%u",
+				*size, swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_OFFSET);
+			return -EINVAL;
+		}
+		}
+		break;
+	default:
+		CAM_ERR(CAM_CDM, "unsupported cdm_cmd_type type 0%x",
+		cmd_type);
+		ret = -EINVAL;
+		break;
 	}
+
+	return ret;
+}
+
+static int cam_cdm_util_reg_cont_write(void __iomem *base_addr,
+	uint32_t *cmd_buf, uint32_t cmd_buf_size, uint32_t *used_bytes,
+	uint32_t base_array_size,
+	struct cam_soc_reg_map *base_table[CAM_SOC_MAX_BLOCK])
+{
+	int rc;
+	uint32_t *data;
+	struct cdm_regcontinuous_cmd reg_cont;
+	resource_size_t size = 0;
+
+	memcpy(&reg_cont, cmd_buf, sizeof(struct cdm_regcontinuous_cmd));
+	rc = cam_cdm_util_cmd_buf_validation(base_addr, base_array_size, base_table,
+		cmd_buf_size, cmd_buf, (void *)&reg_cont,
+		&size, CAM_CDM_CMD_REG_CONT);
+	if (rc) {
+		CAM_ERR(CAM_CDM, "Validation failed! rc=%d", rc);
+		return rc;
+	}
+
 	data = cmd_buf + cam_cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT);
-	cam_io_memcpy(base_addr + reg_cont->offset,	data,
-		reg_cont->count * sizeof(uint32_t));
 
-	*used_bytes = (reg_cont->count * sizeof(uint32_t)) +
+	cam_io_memcpy(base_addr + reg_cont.offset, data,
+		reg_cont.count * sizeof(uint32_t));
+	*used_bytes = (reg_cont.count * sizeof(uint32_t)) +
 		(4 * cam_cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT));
 
-	return ret;
+	return rc;
 }
 
 static int cam_cdm_util_reg_random_write(void __iomem *base_addr,
-	uint32_t *cmd_buf, uint32_t cmd_buf_size, uint32_t *used_bytes)
+	uint32_t *cmd_buf, uint32_t cmd_buf_size, uint32_t *used_bytes,
+	uint32_t base_array_size,
+	struct cam_soc_reg_map *base_table[CAM_SOC_MAX_BLOCK])
 {
-	uint32_t i;
-	struct cdm_regrandom_cmd *reg_random;
-	uint32_t *data;
-
-	if (!base_addr) {
-		CAM_ERR(CAM_CDM, "invalid base address");
-		return -EINVAL;
+	int i, rc;
+	struct cdm_regrandom_cmd reg_random;
+	uint32_t *data, offset;
+	resource_size_t size = 0;
+
+	memcpy(&reg_random, cmd_buf, sizeof(struct cdm_regrandom_cmd));
+
+	rc = cam_cdm_util_cmd_buf_validation(base_addr, base_array_size, base_table,
+		cmd_buf_size, cmd_buf, (void *)&reg_random,
+		&size, CAM_CDM_CMD_REG_RANDOM);
+	if (rc) {
+		CAM_ERR(CAM_CDM, "Validation failed! rc=%d", rc);
+		return rc;
 	}
 
-	reg_random = (struct cdm_regrandom_cmd *) cmd_buf;
-	if ((!reg_random->count) || (((reg_random->count * (sizeof(uint32_t) * 2)) +
-		cam_cdm_get_cmd_header_size(CAM_CDM_CMD_REG_RANDOM)) >
-			cmd_buf_size)) {
-		CAM_ERR(CAM_CDM, "invalid reg_count  %d cmd_buf_size %d",
-			reg_random->count, cmd_buf_size);
-		return -EINVAL;
-	}
 	data = cmd_buf + cam_cdm_get_cmd_header_size(CAM_CDM_CMD_REG_RANDOM);
 
-	for (i = 0; i < reg_random->count; i++) {
+	for (i = 0; i < reg_random.count; i++) {
+		offset = data[0];
 		CAM_DBG(CAM_CDM, "reg random: offset %pK, value 0x%x",
-			((void __iomem *)(base_addr + data[0])),
+			((void __iomem *)(base_addr + offset)),
 			data[1]);
-		cam_io_w(data[1], base_addr + data[0]);
+		cam_io_w(data[1], base_addr + offset);
 		data += 2;
 	}
 
-	*used_bytes = ((reg_random->count * (sizeof(uint32_t) * 2)) +
+	*used_bytes = ((reg_random.count * (sizeof(uint32_t) * 2)) +
 		(4 * cam_cdm_get_cmd_header_size(CAM_CDM_CMD_REG_RANDOM)));
 
-	return 0;
+	return rc;
 }
 
 static int cam_cdm_util_swd_dmi_write(uint32_t cdm_cmd_type,
 	void __iomem *base_addr, uint32_t *cmd_buf, uint32_t cmd_buf_size,
-	uint32_t *used_bytes)
+	uint32_t *used_bytes, uint32_t base_array_size,
+	struct cam_soc_reg_map *base_table[CAM_SOC_MAX_BLOCK])
 {
-	uint32_t i;
-	struct cdm_dmi_cmd *swd_dmi;
+	int i, rc;
+	struct cdm_dmi_cmd swd_dmi;
 	uint32_t *data;
-
-	swd_dmi = (struct cdm_dmi_cmd *)cmd_buf;
-
-	if (cmd_buf_size < (cam_cdm_required_size_dmi() + swd_dmi->length + 1)) {
-		CAM_ERR(CAM_CDM, "invalid CDM_SWD_DMI length %d",
-			swd_dmi->length + 1);
-		return -EINVAL;
+	resource_size_t size = 0;
+
+	memcpy(&swd_dmi, cmd_buf, sizeof(struct cdm_dmi_cmd));
+	rc = cam_cdm_util_cmd_buf_validation(base_addr, base_array_size, base_table,
+		cmd_buf_size, cmd_buf, (void *)&swd_dmi,
+		&size, cdm_cmd_type);
+	if (rc) {
+		CAM_ERR(CAM_CDM, "Validation failed! rc=%d", rc);
+		return rc;
 	}
+
 	data = cmd_buf + cam_cdm_required_size_dmi();
 
 	if (cdm_cmd_type == CAM_CDM_CMD_SWD_DMI_64) {
-		for (i = 0; i < (swd_dmi->length + 1)/8; i++) {
+		for (i = 0; i < (swd_dmi.length + 1)/8; i++) {
 			cam_io_w_mb(data[0], base_addr +
-				swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_LO_OFFSET);
+				swd_dmi.DMIAddr + CAM_CDM_DMI_DATA_LO_OFFSET);
 			cam_io_w_mb(data[1], base_addr +
-				swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_HI_OFFSET);
+				swd_dmi.DMIAddr + CAM_CDM_DMI_DATA_HI_OFFSET);
 			data += 2;
 		}
 	} else if (cdm_cmd_type == CAM_CDM_CMD_DMI) {
-		for (i = 0; i < (swd_dmi->length + 1)/4; i++) {
+		for (i = 0; i < (swd_dmi.length + 1)/4; i++) {
 			cam_io_w_mb(data[0], base_addr +
-				swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_OFFSET);
+				swd_dmi.DMIAddr + CAM_CDM_DMI_DATA_OFFSET);
 			data += 1;
 		}
 	} else {
-		for (i = 0; i < (swd_dmi->length + 1)/4; i++) {
+		for (i = 0; i < (swd_dmi.length + 1)/4; i++) {
 			cam_io_w_mb(data[0], base_addr +
-				swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_LO_OFFSET);
+				swd_dmi.DMIAddr + CAM_CDM_DMI_DATA_LO_OFFSET);
 			data += 1;
 		}
 	}
-	*used_bytes = (4 * cam_cdm_required_size_dmi()) + swd_dmi->length + 1;
+	*used_bytes = (4 * cam_cdm_required_size_dmi()) + swd_dmi.length + 1;
 
-	return 0;
+	return rc;
 }
 
 int cam_cdm_util_cmd_buf_write(void __iomem **current_device_base,
@@ -615,7 +753,8 @@ int cam_cdm_util_cmd_buf_write(void __iomem **current_device_base,
 		switch (cdm_cmd_type) {
 		case CAM_CDM_CMD_REG_CONT: {
 			ret = cam_cdm_util_reg_cont_write(*current_device_base,
-				cmd_buf, cmd_buf_size, &used_bytes);
+				cmd_buf, cmd_buf_size, &used_bytes,
+				base_array_size, base_table);
 			if (ret)
 				break;
 
@@ -628,7 +767,7 @@ int cam_cdm_util_cmd_buf_write(void __iomem **current_device_base,
 		case CAM_CDM_CMD_REG_RANDOM: {
 			ret = cam_cdm_util_reg_random_write(
 				*current_device_base, cmd_buf, cmd_buf_size,
-				&used_bytes);
+				&used_bytes, base_array_size, base_table);
 			if (ret)
 				break;
 
@@ -650,7 +789,7 @@ int cam_cdm_util_cmd_buf_write(void __iomem **current_device_base,
 			}
 			ret = cam_cdm_util_swd_dmi_write(cdm_cmd_type,
 				*current_device_base, cmd_buf, cmd_buf_size,
-				&used_bytes);
+				&used_bytes, base_array_size, base_table);
 			if (ret)
 				break;