mirror of
				https://github.com/Divested-Mobile/DivestOS-Build.git
				synced 2025-10-31 06:31:39 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			394 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			394 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From d567c744898f67e1c54db5339f41815d02f3d59e Mon Sep 17 00:00:00 2001
 | |
| From: Andrew Chant <achant@google.com>
 | |
| Date: Sun, 13 Nov 2016 14:29:08 -0800
 | |
| Subject: [PATCH] input: synaptics_dsx: add update bounds checks.
 | |
| 
 | |
| Firmware updates contain offsets that are parsed
 | |
| by the kernel driver.  Ensure all offsets are within
 | |
| the bounds of the firmware update.
 | |
| 
 | |
| TESTED:
 | |
| successfully parsed update firmware on device boot.
 | |
| 
 | |
| Bug: 31525965
 | |
| Bug: 31968442
 | |
| Change-Id: If66dd1a837d0606250db6f1c75c89747d106541c
 | |
| Signed-off-by: Andrew Chant <achant@google.com>
 | |
| ---
 | |
|  .../synaptics_dsx25/synaptics_dsx_fw_update.c      | 172 +++++++++++++++++----
 | |
|  1 file changed, 144 insertions(+), 28 deletions(-)
 | |
| 
 | |
| diff --git a/drivers/input/touchscreen/synaptics_dsx25/synaptics_dsx_fw_update.c b/drivers/input/touchscreen/synaptics_dsx25/synaptics_dsx_fw_update.c
 | |
| index ff82a4f3a55e8..323f65891b458 100755
 | |
| --- a/drivers/input/touchscreen/synaptics_dsx25/synaptics_dsx_fw_update.c
 | |
| +++ b/drivers/input/touchscreen/synaptics_dsx25/synaptics_dsx_fw_update.c
 | |
| @@ -691,6 +691,22 @@ static int __init early_parse_tp_panel_cmdline(char *arg)
 | |
|  }
 | |
|  early_param("mdss_mdp.panel", early_parse_tp_panel_cmdline);
 | |
|  
 | |
| +/* Check offset + size <= bound.  1 if in bounds, 0 otherwise. */
 | |
| +static int in_bounds(unsigned long offset,
 | |
| +		     unsigned long size,
 | |
| +		     unsigned long bound)
 | |
| +{
 | |
| +	if (offset > bound || size > bound) {
 | |
| +		pr_err("%s: %lu or %lu > %lu\n", __func__, offset, size, bound);
 | |
| +		return 0;
 | |
| +	}
 | |
| +	if (offset > (bound - size)) {
 | |
| +		pr_err("%s: %lu > %lu - %lu\n", __func__, offset, size, bound);
 | |
| +		return 0;
 | |
| +	}
 | |
| +	return 1;
 | |
| +}
 | |
| +
 | |
|  static unsigned int le_to_uint(const unsigned char *ptr)
 | |
|  {
 | |
|  	return (unsigned int)ptr[0] +
 | |
| @@ -770,8 +786,10 @@ static void fwu_compare_partition_tables(void)
 | |
|  	return;
 | |
|  }
 | |
|  
 | |
| -static void fwu_parse_partition_table(const unsigned char *partition_table,
 | |
| -		struct block_count *blkcount, struct physical_address *phyaddr)
 | |
| +static int fwu_parse_partition_table(const unsigned char *partition_table,
 | |
| +				     unsigned long len,
 | |
| +				     struct block_count *blkcount,
 | |
| +				     struct physical_address *phyaddr)
 | |
|  {
 | |
|  	unsigned char ii;
 | |
|  	unsigned char index;
 | |
| @@ -784,6 +802,11 @@ static void fwu_parse_partition_table(const unsigned char *partition_table,
 | |
|  	tp_log_debug("%s: in!\n",__func__);
 | |
|  	for (ii = 0; ii < fwu->partitions; ii++) {
 | |
|  		index = ii * 8 + 2;
 | |
| +		if (!in_bounds(index, sizeof(*ptable), len)) {
 | |
| +			pr_err("%s: %d/%d not in bounds\n", __func__, ii,
 | |
| +			       fwu->partitions);
 | |
| +			return -EINVAL;
 | |
| +		}
 | |
|  		ptable = (struct partition_table *)&partition_table[index];
 | |
|  		partition_length = ptable->partition_length_15_8 << 8 |
 | |
|  				ptable->partition_length_7_0;
 | |
| @@ -792,7 +815,7 @@ static void fwu_parse_partition_table(const unsigned char *partition_table,
 | |
|  		dev_dbg(rmi4_data->pdev->dev.parent,
 | |
|  				"%s: Partition entry %d:\n",
 | |
|  				__func__, ii);
 | |
| -		for (offset = 0; offset < 8; offset++) {
 | |
| +		for (offset = 0; offset < sizeof(*ptable); offset++) {
 | |
|  			dev_dbg(rmi4_data->pdev->dev.parent,
 | |
|  					"%s: 0x%02x\n",
 | |
|  					__func__,
 | |
| @@ -854,28 +877,36 @@ static void fwu_parse_partition_table(const unsigned char *partition_table,
 | |
|  		};
 | |
|  	}
 | |
|  
 | |
| -	return;
 | |
| +	return 0;
 | |
|  }
 | |
|  
 | |
| -static void fwu_parse_image_header_10_bl_container(const unsigned char *image)
 | |
| +static int fwu_parse_image_header_10_bl_container(const unsigned char *image)
 | |
|  {
 | |
|  	unsigned char ii;
 | |
|  	unsigned char num_of_containers;
 | |
|  	unsigned int addr;
 | |
|  	unsigned int container_id;
 | |
| +	unsigned int content_offset;
 | |
|  	unsigned int length;
 | |
|  	const unsigned char *content;
 | |
|  	struct container_descriptor *descriptor;
 | |
|  
 | |
| +	if (fwu->img.bootloader.size < 4)
 | |
| +		return -ENOENT;
 | |
|  	num_of_containers = (fwu->img.bootloader.size - 4) / 4;
 | |
|  
 | |
|  	for (ii = 1; ii <= num_of_containers; ii++) {
 | |
|  		addr = le_to_uint(fwu->img.bootloader.data + (ii * 4));
 | |
| +		if (!in_bounds(addr, sizeof(*descriptor), fwu->image_size))
 | |
| +			return -EINVAL;
 | |
|  		descriptor = (struct container_descriptor *)(image + addr);
 | |
|  		container_id = descriptor->container_id[0] |
 | |
|  				descriptor->container_id[1] << 8;
 | |
| -		content = image + le_to_uint(descriptor->content_address);
 | |
| +		content_offset = le_to_uint(descriptor->content_address);
 | |
|  		length = le_to_uint(descriptor->content_length);
 | |
| +		if (!in_bounds(content_offset, length, fwu->image_size))
 | |
| +			return -EINVAL;
 | |
| +		content = image + content_offset;
 | |
|  		switch (container_id) {
 | |
|  		case BL_CONFIG_CONTAINER:
 | |
|  		case GLOBAL_PARAMETERS_CONTAINER:
 | |
| @@ -892,10 +923,10 @@ static void fwu_parse_image_header_10_bl_container(const unsigned char *image)
 | |
|  		};
 | |
|  	}
 | |
|  
 | |
| -	return;
 | |
| +	return 0;
 | |
|  }
 | |
|  
 | |
| -static void fwu_parse_image_header_10(void)
 | |
| +static int fwu_parse_image_header_10(void)
 | |
|  {
 | |
|  	unsigned char ii;
 | |
|  	unsigned char num_of_containers;
 | |
| @@ -903,6 +934,7 @@ static void fwu_parse_image_header_10(void)
 | |
|  	unsigned int offset;
 | |
|  	unsigned int container_id;
 | |
|  	unsigned int length;
 | |
| +	unsigned int content_offset;
 | |
|  	const unsigned char *image;
 | |
|  	const unsigned char *content;
 | |
|  	struct container_descriptor *descriptor;
 | |
| @@ -911,25 +943,35 @@ static void fwu_parse_image_header_10(void)
 | |
|  	tp_log_debug("%s: in!\n",__func__);
 | |
|  	image = fwu->image;
 | |
|  	header = (struct image_header_10 *)image;
 | |
| -
 | |
| +	if (fwu->image_size < sizeof(*header))
 | |
| +		return -EINVAL;
 | |
|  	fwu->img.checksum = le_to_uint(header->checksum);
 | |
|  
 | |
|  	/* address of top level container */
 | |
|  	offset = le_to_uint(header->top_level_container_start_addr);
 | |
|  	descriptor = (struct container_descriptor *)(image + offset);
 | |
| +	if (!in_bounds(offset, sizeof(*descriptor), fwu->image_size))
 | |
| +		return -EINVAL;
 | |
|  
 | |
|  	/* address of top level container content */
 | |
|  	offset = le_to_uint(descriptor->content_address);
 | |
|  	num_of_containers = le_to_uint(descriptor->content_length) / 4;
 | |
|  
 | |
|  	for (ii = 0; ii < num_of_containers; ii++) {
 | |
| +		if (!in_bounds(offset, 4, fwu->image_size))
 | |
| +			return -EINVAL;
 | |
|  		addr = le_to_uint(image + offset);
 | |
|  		offset += 4;
 | |
| +		if (!in_bounds(addr, sizeof(*descriptor), fwu->image_size))
 | |
| +			return -EINVAL;
 | |
|  		descriptor = (struct container_descriptor *)(image + addr);
 | |
|  		container_id = descriptor->container_id[0] |
 | |
|  				descriptor->container_id[1] << 8;
 | |
| -		content = image + le_to_uint(descriptor->content_address);
 | |
| +		content_offset = le_to_uint(descriptor->content_address);
 | |
|  		length = le_to_uint(descriptor->content_length);
 | |
| +		if (!in_bounds(content_offset, length, fwu->image_size))
 | |
| +			return -EINVAL;
 | |
| +		content = image + content_offset;
 | |
|  		switch (container_id) {
 | |
|  		case UI_CONTAINER:
 | |
|  		case CORE_CODE_CONTAINER:
 | |
| @@ -945,7 +987,8 @@ static void fwu_parse_image_header_10(void)
 | |
|  			fwu->img.bl_version = *content;
 | |
|  			fwu->img.bootloader.data = content;
 | |
|  			fwu->img.bootloader.size = length;
 | |
| -			fwu_parse_image_header_10_bl_container(image);
 | |
| +			if (fwu_parse_image_header_10_bl_container(image))
 | |
| +				return -EINVAL;
 | |
|  			break;
 | |
|  		case GUEST_CODE_CONTAINER:
 | |
|  			fwu->img.contains_guest_code = true;
 | |
| @@ -964,6 +1007,8 @@ static void fwu_parse_image_header_10(void)
 | |
|  			break;
 | |
|  		case GENERAL_INFORMATION_CONTAINER:
 | |
|  			fwu->img.contains_firmware_id = true;
 | |
| +			if (length < 4 + 4)
 | |
| +				return -EINVAL;
 | |
|  			fwu->img.firmware_id = le_to_uint(content + 4);
 | |
|  			break;
 | |
|  		default:
 | |
| @@ -971,10 +1016,10 @@ static void fwu_parse_image_header_10(void)
 | |
|  		}
 | |
|  	}
 | |
|  
 | |
| -	return;
 | |
| +	return 0;
 | |
|  }
 | |
|  
 | |
| -static void fwu_parse_image_header_05_06(void)
 | |
| +static int fwu_parse_image_header_05_06(void)
 | |
|  {
 | |
|  	int retval;
 | |
|  	const unsigned char *image;
 | |
| @@ -983,6 +1028,8 @@ static void fwu_parse_image_header_05_06(void)
 | |
|  
 | |
|  	tp_log_debug("%s: in!\n",__func__);
 | |
|  	image = fwu->image;
 | |
| +	if (fwu->image_size < sizeof(*header))
 | |
| +		return -EINVAL;
 | |
|  	header = (struct image_header_05_06 *)image;
 | |
|  
 | |
|  	fwu->img.checksum = le_to_uint(header->checksum);
 | |
| @@ -995,18 +1042,51 @@ static void fwu_parse_image_header_05_06(void)
 | |
|  
 | |
|  	fwu->img.ui_firmware.size = le_to_uint(header->firmware_size);
 | |
|  	if (fwu->img.ui_firmware.size) {
 | |
| -		fwu->img.ui_firmware.data = image + IMAGE_AREA_OFFSET;
 | |
| -		if (fwu->img.contains_bootloader)
 | |
| -			fwu->img.ui_firmware.data += fwu->img.bootloader_size;
 | |
| +		unsigned int ui_firmware_offset = IMAGE_AREA_OFFSET;
 | |
| +
 | |
| +		if (fwu->img.contains_bootloader) {
 | |
| +			if (!in_bounds(ui_firmware_offset,
 | |
| +				       fwu->img.bootloader_size,
 | |
| +				       fwu->image_size)) {
 | |
| +				return -EINVAL;
 | |
| +			}
 | |
| +			ui_firmware_offset += fwu->img.bootloader_size;
 | |
| +		}
 | |
| +		if (!in_bounds(ui_firmware_offset,
 | |
| +			       fwu->img.ui_firmware.size,
 | |
| +			       fwu->image_size)) {
 | |
| +			return -EINVAL;
 | |
| +		}
 | |
| +		fwu->img.ui_firmware.data = image + ui_firmware_offset;
 | |
|  	}
 | |
|  
 | |
| -	if ((fwu->img.bl_version == BL_V6) && header->options_tddi)
 | |
| +	if ((fwu->img.bl_version == BL_V6) && header->options_tddi) {
 | |
| +		if (!in_bounds(IMAGE_AREA_OFFSET,
 | |
| +			       fwu->img.ui_firmware.size,
 | |
| +			       fwu->image_size)) {
 | |
| +			return -EINVAL;
 | |
| +		}
 | |
|  		fwu->img.ui_firmware.data = image + IMAGE_AREA_OFFSET;
 | |
| +	}
 | |
|  
 | |
|  	fwu->img.ui_config.size = le_to_uint(header->config_size);
 | |
|  	if (fwu->img.ui_config.size) {
 | |
| -		fwu->img.ui_config.data = fwu->img.ui_firmware.data +
 | |
| +		unsigned int ui_firmware_end;
 | |
| +
 | |
| +		if (fwu->img.ui_firmware.data < image)
 | |
| +			return -EINVAL;
 | |
| +		if (!in_bounds(fwu->img.ui_firmware.data - image,
 | |
| +			       fwu->img.ui_firmware.size,
 | |
| +			       fwu->image_size)) {
 | |
| +			return -EINVAL;
 | |
| +		}
 | |
| +		ui_firmware_end = fwu->img.ui_firmware.data - image +
 | |
|  				fwu->img.ui_firmware.size;
 | |
| +		if (!in_bounds(ui_firmware_end, fwu->img.ui_config.size,
 | |
| +			       fwu->image_size)) {
 | |
| +			return -EINVAL;
 | |
| +		}
 | |
| +		fwu->img.ui_config.data = image + ui_firmware_end;
 | |
|  	}
 | |
|  
 | |
|  	if ((fwu->img.bl_version == BL_V5 && fwu->img.contains_bootloader) ||
 | |
| @@ -1018,6 +1098,11 @@ static void fwu_parse_image_header_05_06(void)
 | |
|  	if (fwu->img.contains_disp_config) {
 | |
|  		fwu->img.disp_config_offset = le_to_uint(header->dsp_cfg_addr);
 | |
|  		fwu->img.dp_config.size = le_to_uint(header->dsp_cfg_size);
 | |
| +		if (!in_bounds(fwu->img.disp_config_offset,
 | |
| +			       fwu->img.dp_config.size,
 | |
| +			       fwu->image_size)) {
 | |
| +			return -EINVAL;
 | |
| +		}
 | |
|  		fwu->img.dp_config.data = image + fwu->img.disp_config_offset;
 | |
|  	} else {
 | |
|  		retval = secure_memcpy(fwu->img.cstmr_product_id,
 | |
| @@ -1050,28 +1135,52 @@ static void fwu_parse_image_header_05_06(void)
 | |
|  	fwu->img.product_id[PRODUCT_ID_SIZE] = 0;
 | |
|  
 | |
|  	fwu->img.lockdown.size = LOCKDOWN_SIZE;
 | |
| +	if (LOCKDOWN_SIZE > IMAGE_AREA_OFFSET)
 | |
| +		return -EINVAL;
 | |
| +	if (fwu->image_size < IMAGE_AREA_OFFSET)
 | |
| +		return -EINVAL;
 | |
|  	fwu->img.lockdown.data = image + IMAGE_AREA_OFFSET - LOCKDOWN_SIZE;
 | |
|  
 | |
| -	return;
 | |
| +	return 0;
 | |
|  }
 | |
|  
 | |
|  static int fwu_parse_image_info(void)
 | |
|  {
 | |
|  	struct image_header_10 *header;
 | |
|  	struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
 | |
| +	unsigned int image_size = 0;
 | |
|  
 | |
| -	tp_log_debug("%s: in!\n",__func__);
 | |
| +	tp_log_debug("%s: in!\n", __func__);
 | |
|  	header = (struct image_header_10 *)fwu->image;
 | |
| +	if (!header) {
 | |
| +		tp_log_debug("%s: Invalid header\n", __func__);
 | |
| +		return -EINVAL;
 | |
| +	}
 | |
|  
 | |
| +	image_size = fwu->image_size;
 | |
| +	if (image_size < sizeof(struct image_header_05_06) &&
 | |
| +	    image_size < sizeof(struct image_header_10)) {
 | |
| +		tp_log_debug("header too small: %u < (%lu, %lu)",
 | |
| +			     image_size, sizeof(struct image_header_05_06),
 | |
| +			     sizeof(struct image_header_10));
 | |
| +		return -EINVAL;
 | |
| +	}
 | |
| +	/* This is clearing img, not image. */
 | |
|  	memset(&fwu->img, 0x00, sizeof(fwu->img));
 | |
|  
 | |
|  	switch (header->major_header_version) {
 | |
|  	case IMAGE_HEADER_VERSION_10:
 | |
| -		fwu_parse_image_header_10();
 | |
| +		if (fwu_parse_image_header_10()) {
 | |
| +			tp_log_debug("%s:error parsing v10 header\n", __func__);
 | |
| +			return -EINVAL;
 | |
| +		}
 | |
|  		break;
 | |
|  	case IMAGE_HEADER_VERSION_05:
 | |
|  	case IMAGE_HEADER_VERSION_06:
 | |
| -		fwu_parse_image_header_05_06();
 | |
| +		if (fwu_parse_image_header_05_06()) {
 | |
| +			tp_log_debug("%s:error parsing v56 header\n", __func__);
 | |
| +			return -EINVAL;
 | |
| +		}
 | |
|  		break;
 | |
|  	default:
 | |
|  		dev_err(rmi4_data->pdev->dev.parent,
 | |
| @@ -1088,9 +1197,13 @@ static int fwu_parse_image_info(void)
 | |
|  			return -EINVAL;
 | |
|  		}
 | |
|  
 | |
| -		fwu_parse_partition_table(fwu->img.fl_config.data,
 | |
| -				&fwu->img.blkcount, &fwu->img.phyaddr);
 | |
| -
 | |
| +		if (fwu_parse_partition_table(fwu->img.fl_config.data,
 | |
| +					      fwu->img.fl_config.size,
 | |
| +					      &fwu->img.blkcount,
 | |
| +					      &fwu->img.phyaddr)) {
 | |
| +			tp_log_debug("%s:Error parsing ptable\n", __func__);
 | |
| +			return -EINVAL;
 | |
| +		}
 | |
|  		fwu_compare_partition_tables();
 | |
|  	} else {
 | |
|  		fwu->new_partition_table = false;
 | |
| @@ -1669,7 +1782,9 @@ static int fwu_read_f34_v7_queries(void)
 | |
|  		return retval;
 | |
|  	}
 | |
|  
 | |
| -	fwu_parse_partition_table(ptable, &fwu->blkcount, &fwu->phyaddr);
 | |
| +	if (fwu_parse_partition_table(ptable, fwu->partition_table_bytes,
 | |
| +				      &fwu->blkcount, &fwu->phyaddr))
 | |
| +		return -EINVAL;
 | |
|  
 | |
|  	return 0;
 | |
|  }
 | |
| @@ -3330,6 +3445,7 @@ static int fwu_start_reflash(void)
 | |
|  				__func__, fw_entry->size);
 | |
|  
 | |
|  		fwu->image = fw_entry->data;
 | |
| +		fwu->image_size = fw_entry->size;
 | |
|  	}
 | |
|  
 | |
|  	retval = fwu_parse_image_info();
 | |
| @@ -4203,8 +4319,8 @@ static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
 | |
|  			&fwu->fwu_work);
 | |
|  #endif
 | |
|  
 | |
| -        retval = sysfs_create_bin_file(&rmi4_data->input_dev->dev.kobj,
 | |
| -			&dev_attr_data);
 | |
| +	retval = sysfs_create_bin_file(&rmi4_data->input_dev->dev.kobj,
 | |
| +				       &dev_attr_data);
 | |
|  	if (retval < 0) {
 | |
|  		dev_err(rmi4_data->pdev->dev.parent,
 | |
|  				"%s: Failed to create sysfs bin file\n",
 | 
