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.
This is a secondary patch for the dsx version of the
driver. Both drivers are loaded at boot.
Bug: 31525965
Bug: 31968442
Change-Id: Icbe28aed0917ddfd089285ab25dbc5826a15be2f
Signed-off-by: Andrew Chant <achant@google.com>
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c
index 1f7409e..b22eb1e 100644
--- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c
@@ -347,6 +347,22 @@
DECLARE_COMPLETION(fwu_dsx_remove_complete);
DEFINE_MUTEX(dsx_fwu_sysfs_mutex);
+/* 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 extract_uint_le(const unsigned char *ptr)
{
return (unsigned int)ptr[0] +
@@ -363,10 +379,17 @@
(unsigned int)ptr[0] * 0x1000000;
}
-static void parse_header(struct image_header_data *header,
- const unsigned char *fw_image)
+static int parse_header(struct image_header_data *header,
+ const unsigned char *fw_image,
+ const unsigned long fw_image_len)
{
struct image_header *data = (struct image_header *)fw_image;
+ if (fw_image_len < sizeof(*data)) {
+ dev_err(fwu->rmi4_data->pdev->dev.parent,
+ "%s: update too small\n",
+ __func__);
+ return -EINVAL;
+ }
header->checksum = extract_uint_le(data->checksum);
@@ -386,7 +409,7 @@
if (header->contains_firmware_id)
header->firmware_id = extract_uint_le(data->firmware_id);
- return;
+ return 0;
}
static int fwu_read_f01_device_status(struct f01_device_status *status)
@@ -1184,9 +1207,29 @@
/* Jump to the config area if given a packrat image */
if ((fwu->config_area == UI_CONFIG_AREA) &&
(fwu->config_size != fwu->image_size)) {
- parse_header(&header, fwu->ext_data_source);
+ if (parse_header(&header,
+ fwu->ext_data_source,
+ fwu->image_size)) {
+ return -EINVAL;
+ }
if (header.config_size) {
+ if (!in_bounds(FW_IMAGE_OFFSET,
+ header.firmware_size,
+ fwu->image_size)) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Firmware out of bounds\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (!in_bounds(FW_IMAGE_OFFSET + header.firmware_size,
+ header.config_size,
+ fwu->image_size)) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Config out of bounds\n",
+ __func__);
+ return -EINVAL;
+ }
fwu->config_data = fwu->ext_data_source +
FW_IMAGE_OFFSET +
header.firmware_size;
@@ -1363,6 +1406,7 @@
struct image_header_data header;
struct f01_device_status f01_device_status;
const unsigned char *fw_image;
+ unsigned long fw_image_len = 0;
const struct firmware *fw_entry = NULL;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
@@ -1379,6 +1423,7 @@
if (fwu->ext_data_source) {
fw_image = fwu->ext_data_source;
+ fw_image_len = fwu->image_size;
} else {
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Requesting firmware image %s\n",
@@ -1399,13 +1444,20 @@
__func__, fw_entry->size);
fw_image = fw_entry->data;
+ fw_image_len = fw_entry->size;
}
- parse_header(&header, fw_image);
+ if (parse_header(&header, fw_image, fw_image_len)) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: couldn't parse header\n",
+ __func__);
+ retval = -EINVAL;
+ goto exit;
+ }
if (fwu->bl_version != header.bootloader_version) {
dev_err(rmi4_data->pdev->dev.parent,
- "%s: Bootloader version mismatch\n",
+ "%s: bootloader version mismatch\n",
__func__);
retval = -EINVAL;
goto exit;
@@ -1441,9 +1493,26 @@
}
}
- if (header.firmware_size)
+ if (header.firmware_size) {
+ if (!in_bounds(FW_IMAGE_OFFSET,
+ header.firmware_size,
+ fw_image_len)) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Firmware out of bounds\n",
+ __func__);
+ return -EINVAL;
+ }
fwu->firmware_data = fw_image + FW_IMAGE_OFFSET;
+ }
if (header.config_size) {
+ if (!in_bounds(FW_IMAGE_OFFSET + header.firmware_size,
+ header.config_size,
+ fw_image_len)) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Config out of bounds\n",
+ __func__);
+ return -EINVAL;
+ }
fwu->config_data = fw_image + FW_IMAGE_OFFSET +
header.firmware_size;
}