mirror of
https://github.com/Divested-Mobile/DivestOS-Build.git
synced 2024-10-01 01:35:54 -04:00
455 lines
15 KiB
Diff
455 lines
15 KiB
Diff
From 11ab3add6cfb1ef752ac38adf1b4bf15617772e9 Mon Sep 17 00:00:00 2001
|
|
From: Andrew Chant <achant@google.com>
|
|
Date: Tue, 8 Nov 2016 15:19:32 -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: Forced a firmware update by removing
|
|
same-firmware check. Firmware update succeeded.
|
|
|
|
Bug: 31525965
|
|
Bug: 31968442
|
|
Change-Id: I287f494d973868f6be28799bc2613ff2201b0717
|
|
Signed-off-by: Andrew Chant <achant@google.com>
|
|
---
|
|
.../synaptics_dsx_fw_update.c | 183 +++++++++++++++++----
|
|
1 file changed, 154 insertions(+), 29 deletions(-)
|
|
|
|
diff --git a/drivers/input/touchscreen/synaptics_dsx_htc_2.6/synaptics_dsx_fw_update.c b/drivers/input/touchscreen/synaptics_dsx_htc_2.6/synaptics_dsx_fw_update.c
|
|
index 05f13b427739b..f7d5dbdd69b53 100644
|
|
--- a/drivers/input/touchscreen/synaptics_dsx_htc_2.6/synaptics_dsx_fw_update.c
|
|
+++ b/drivers/input/touchscreen/synaptics_dsx_htc_2.6/synaptics_dsx_fw_update.c
|
|
@@ -771,6 +771,21 @@ static struct synaptics_rmi4_fwu_handle *fwu;
|
|
DECLARE_COMPLETION(fwu_remove_complete);
|
|
DEFINE_MUTEX(fwu_sysfs_mutex);
|
|
|
|
+/* Check offset + size <= bound. true if in bounds, false otherwise. */
|
|
+static bool 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 false;
|
|
+ }
|
|
+ if (offset > (bound - size)) {
|
|
+ pr_err("%s: %lu > %lu - %lu\n", __func__, offset, size, bound);
|
|
+ return false;
|
|
+ }
|
|
+ return true;
|
|
+}
|
|
+
|
|
#ifdef HTC_FEATURE
|
|
static uint32_t syn_crc(uint16_t *data, uint32_t len)
|
|
{
|
|
@@ -966,8 +981,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;
|
|
@@ -979,6 +996,11 @@ static void fwu_parse_partition_table(const unsigned char *partition_table,
|
|
|
|
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;
|
|
@@ -987,7 +1009,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__,
|
|
@@ -1077,16 +1099,17 @@ static void fwu_parse_partition_table(const unsigned char *partition_table,
|
|
};
|
|
}
|
|
|
|
- return;
|
|
+ return 0;
|
|
}
|
|
|
|
-static void fwu_parse_image_header_10_utility(const unsigned char *image)
|
|
+static int fwu_parse_image_header_10_utility(const unsigned char *image)
|
|
{
|
|
unsigned char ii;
|
|
unsigned char num_of_containers;
|
|
unsigned int addr;
|
|
unsigned int container_id;
|
|
unsigned int length;
|
|
+ unsigned int content_offset;
|
|
const unsigned char *content;
|
|
struct container_descriptor *descriptor;
|
|
|
|
@@ -1099,15 +1122,22 @@ static void fwu_parse_image_header_10_utility(const unsigned char *image)
|
|
if (ii >= MAX_UTILITY_PARAMS)
|
|
continue;
|
|
addr = le_to_uint(fwu->img.utility.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 UTILITY_PARAMETER_CONTAINER:
|
|
fwu->img.utility_param[ii].data = content;
|
|
fwu->img.utility_param[ii].size = length;
|
|
+ if (length < sizeof(content[0]))
|
|
+ return -EINVAL;
|
|
fwu->img.utility_param_id[ii] = content[0];
|
|
break;
|
|
default:
|
|
@@ -1115,28 +1145,36 @@ static void fwu_parse_image_header_10_utility(const unsigned char *image)
|
|
};
|
|
}
|
|
|
|
- return;
|
|
+ return 0;
|
|
}
|
|
|
|
-static void fwu_parse_image_header_10_bootloader(const unsigned char *image)
|
|
+static int fwu_parse_image_header_10_bootloader(const unsigned char *image)
|
|
{
|
|
unsigned char ii;
|
|
unsigned char num_of_containers;
|
|
unsigned int addr;
|
|
unsigned int container_id;
|
|
unsigned int length;
|
|
+ unsigned int content_offset;
|
|
const unsigned char *content;
|
|
struct container_descriptor *descriptor;
|
|
|
|
+ if (fwu->img.bootloader.size < 4)
|
|
+ return -EINVAL;
|
|
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_IMAGE_CONTAINER:
|
|
fwu->img.bl_image.data = content;
|
|
@@ -1157,29 +1195,36 @@ static void fwu_parse_image_header_10_bootloader(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;
|
|
unsigned int addr;
|
|
unsigned int offset;
|
|
+ unsigned int content_offset;
|
|
unsigned int container_id;
|
|
unsigned int length;
|
|
+ unsigned int image_size;
|
|
const unsigned char *image;
|
|
const unsigned char *content;
|
|
struct container_descriptor *descriptor;
|
|
struct image_header_10 *header;
|
|
|
|
image = fwu->image;
|
|
+ image_size = fwu->image_size;
|
|
+ if (image_size < sizeof(*header))
|
|
+ return -EINVAL;
|
|
header = (struct image_header_10 *)image;
|
|
|
|
fwu->img.checksum = le_to_uint(header->checksum);
|
|
|
|
/* address of top level container */
|
|
offset = le_to_uint(header->top_level_container_start_addr);
|
|
+ if (!in_bounds(offset, sizeof(*descriptor), image_size))
|
|
+ return -EINVAL;
|
|
descriptor = (struct container_descriptor *)(image + offset);
|
|
|
|
/* address of top level container content */
|
|
@@ -1187,13 +1232,20 @@ static void fwu_parse_image_header_10(void)
|
|
num_of_containers = le_to_uint(descriptor->content_length) / 4;
|
|
|
|
for (ii = 0; ii < num_of_containers; ii++) {
|
|
+ if (!in_bounds(offset, 4, image_size))
|
|
+ return -EINVAL;
|
|
addr = le_to_uint(image + offset);
|
|
offset += 4;
|
|
+ if (!in_bounds(addr, sizeof(*descriptor), 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, image_size))
|
|
+ return -EINVAL;
|
|
+ content = image + content_offset;
|
|
switch (container_id) {
|
|
case UI_CONTAINER:
|
|
case CORE_CODE_CONTAINER:
|
|
@@ -1209,12 +1261,14 @@ 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_bootloader(image);
|
|
+ if (fwu_parse_image_header_10_bootloader(image))
|
|
+ return -EINVAL;
|
|
break;
|
|
case UTILITY_CONTAINER:
|
|
fwu->img.utility.data = content;
|
|
fwu->img.utility.size = length;
|
|
- fwu_parse_image_header_10_utility(image);
|
|
+ if (fwu_parse_image_header_10_utility(image))
|
|
+ return -EINVAL;
|
|
break;
|
|
case GUEST_CODE_CONTAINER:
|
|
fwu->img.contains_guest_code = true;
|
|
@@ -1239,6 +1293,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:
|
|
@@ -1246,10 +1302,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;
|
|
@@ -1257,6 +1313,8 @@ static void fwu_parse_image_header_05_06(void)
|
|
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
|
|
|
|
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);
|
|
@@ -1269,18 +1327,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) ||
|
|
@@ -1292,6 +1383,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,
|
|
@@ -1323,28 +1419,41 @@ static void fwu_parse_image_header_05_06(void)
|
|
}
|
|
fwu->img.product_id[PRODUCT_ID_SIZE] = 0;
|
|
|
|
+ if (LOCKDOWN_SIZE > IMAGE_AREA_OFFSET)
|
|
+ return -EINVAL;
|
|
+ if (fwu->image_size < IMAGE_AREA_OFFSET)
|
|
+ return -EINVAL;
|
|
fwu->img.lockdown.size = LOCKDOWN_SIZE;
|
|
fwu->img.lockdown.data = image + IMAGE_AREA_OFFSET - LOCKDOWN_SIZE;
|
|
|
|
- return;
|
|
+ return 0;
|
|
}
|
|
|
|
static int fwu_parse_image_info(void)
|
|
{
|
|
+ int parse_retval;
|
|
struct image_header_10 *header;
|
|
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
|
|
+ unsigned int image_size = 0;
|
|
|
|
header = (struct image_header_10 *)fwu->image;
|
|
-
|
|
+ if (!header)
|
|
+ return -EINVAL;
|
|
+ image_size = fwu->image_size;
|
|
+ if (image_size < sizeof(struct image_header_05_06) &&
|
|
+ image_size < 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();
|
|
+ parse_retval = fwu_parse_image_header_10();
|
|
break;
|
|
case IMAGE_HEADER_VERSION_05:
|
|
case IMAGE_HEADER_VERSION_06:
|
|
- fwu_parse_image_header_05_06();
|
|
+ parse_retval = fwu_parse_image_header_05_06();
|
|
break;
|
|
default:
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
@@ -1353,6 +1462,10 @@ static int fwu_parse_image_info(void)
|
|
return -EINVAL;
|
|
}
|
|
|
|
+ if (parse_retval != 0) {
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) {
|
|
if (!fwu->img.contains_flash_config) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
@@ -1361,9 +1474,12 @@ 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)) {
|
|
+ return -EINVAL;
|
|
+ }
|
|
fwu_compare_partition_tables();
|
|
} else {
|
|
fwu->new_partition_table = false;
|
|
@@ -1980,7 +2096,11 @@ 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)) {
|
|
+ kfree(ptable);
|
|
+ return -EINVAL;
|
|
+ }
|
|
|
|
if (fwu->blkcount.dp_config)
|
|
fwu->flash_properties.has_disp_config = 1;
|
|
@@ -3209,6 +3329,9 @@ static int fwu_write_utility_parameter(void)
|
|
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
|
|
|
|
utility_param_size = fwu->blkcount.utility_param * fwu->block_size;
|
|
+ /* See remaining_size below for reason for '4' */
|
|
+ if (utility_param_size < 4)
|
|
+ return -EINVAL;
|
|
retval = fwu_allocate_read_config_buf(utility_param_size);
|
|
if (retval < 0)
|
|
return retval;
|
|
@@ -4910,6 +5033,7 @@ int synaptics_config_updater(struct synaptics_dsx_board_data *bdata)
|
|
|
|
rmi4_data->stay_awake = true;
|
|
|
|
+ memset(config_id, 0, sizeof(config_id));
|
|
if (fwu->bl_version == BL_V7)
|
|
config_id_size = V7_CONFIG_ID_SIZE;
|
|
else
|
|
@@ -4928,6 +5052,7 @@ int synaptics_config_updater(struct synaptics_dsx_board_data *bdata)
|
|
}
|
|
|
|
memset(str_buf, 0, sizeof(str_buf));
|
|
+ memset(tmp_buf, 0, sizeof(tmp_buf));
|
|
for (ii = 0; ii < config_id_size; ii++) {
|
|
snprintf(tmp_buf, 3, "%02x ", config_id[ii]);
|
|
strlcat(str_buf, tmp_buf, sizeof(str_buf));
|