From d567c744898f67e1c54db5339f41815d02f3d59e Mon Sep 17 00:00:00 2001 From: Andrew Chant 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 --- .../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",