DivestOS/Patches/Linux_CVEs/CVE-2016-6745/ANY/1.patch
2017-10-29 22:14:37 -04:00

445 lines
12 KiB
Diff

From 9397e20764da2fdffdfe20e35cb78211753b83cc Mon Sep 17 00:00:00 2001
From: Andrew Chant <achant@google.com>
Date: Wed, 14 Sep 2016 17:21:48 -0700
Subject: [PATCH] input: synaptics: prevent sysfs races
concurrent sysfs calls on the fw updater can cause
ugly race conditions. Return EBUSY on concurrent sysfs calls.
For sysfs calls which generate deferred work, prevent
the deferred work from running concurrently with other
sysfs calls.
Change-Id: Ie33add946fbcca8309998e4cb7cb01525c667c7e
Signed-off-by: Andrew Chant <achant@google.com>
Bug: 31252388
---
drivers/input/touchscreen/synaptics_fw_update.c | 144 ++++++++++++++++++------
1 file changed, 109 insertions(+), 35 deletions(-)
diff --git a/drivers/input/touchscreen/synaptics_fw_update.c b/drivers/input/touchscreen/synaptics_fw_update.c
index 79b3a780550b8..ffa992b829a5a 100644
--- a/drivers/input/touchscreen/synaptics_fw_update.c
+++ b/drivers/input/touchscreen/synaptics_fw_update.c
@@ -22,6 +22,7 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
+#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/firmware.h>
@@ -296,6 +297,7 @@ struct synaptics_rmi4_fwu_handle {
static struct synaptics_rmi4_fwu_handle *fwu;
DECLARE_COMPLETION(fwu_remove_complete);
+DEFINE_MUTEX(fwu_sysfs_mutex);
static unsigned int extract_uint(const unsigned char *ptr)
{
@@ -1713,34 +1715,47 @@ static ssize_t fwu_sysfs_show_image(struct file *data_file,
char *buf, loff_t pos, size_t count)
{
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+ ssize_t retval;
+
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
if (count < fwu->config_size) {
dev_err(&rmi4_data->i2c_client->dev,
"%s: Not enough space (%zu bytes) in buffer\n",
__func__, count);
- return -EINVAL;
+ retval = -EINVAL;
+ goto show_image_exit;
}
memcpy(buf, fwu->read_config_buf, fwu->config_size);
-
- return fwu->config_size;
+ retval = fwu->config_size;
+show_image_exit:
+ mutex_unlock(&fwu_sysfs_mutex);
+ return retval;
}
static ssize_t fwu_sysfs_store_image(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count)
{
+ ssize_t retval;
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
+
if (!fwu->ext_data_source) {
dev_err(&fwu->rmi4_data->i2c_client->dev,
"Cannot use this without setting imagesize!\n");
- return -EAGAIN;
+ retval = -EAGAIN;
+ goto store_image_exit;
}
if (count > fwu->image_size - fwu->data_pos) {
dev_err(&fwu->rmi4_data->i2c_client->dev,
"%s: Not enough space in buffer\n",
__func__);
- return -EINVAL;
+ retval = -EINVAL;
+ goto store_image_exit;
}
memcpy((void *)(&fwu->ext_data_source[fwu->data_pos]),
@@ -1749,8 +1764,11 @@ static ssize_t fwu_sysfs_store_image(struct file *data_file,
fwu->data_buffer = fwu->ext_data_source;
fwu->data_pos += count;
+ retval = count;
- return count;
+store_image_exit:
+ mutex_unlock(&fwu_sysfs_mutex);
+ return retval;
}
static ssize_t fwu_sysfs_image_name_store(struct device *dev,
@@ -1758,11 +1776,15 @@ static ssize_t fwu_sysfs_image_name_store(struct device *dev,
{
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
char *strptr;
+ ssize_t retval;
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
if (count >= NAME_BUFFER_SIZE) {
dev_err(&rmi4_data->i2c_client->dev,
"Input over %d characters long\n", NAME_BUFFER_SIZE);
- return -EINVAL;
+ retval = -EINVAL;
+ goto image_name_store_exit;
}
strptr = strnstr(buf, ".img",
@@ -1770,21 +1792,32 @@ static ssize_t fwu_sysfs_image_name_store(struct device *dev,
if (!strptr) {
dev_err(&rmi4_data->i2c_client->dev,
"Input is not valid .img file\n");
- return -EINVAL;
+ retval = -EINVAL;
+ goto image_name_store_exit;
}
strlcpy(rmi4_data->fw_image_name, buf, count);
- return count;
+ retval = count;
+
+image_name_store_exit:
+ mutex_unlock(&fwu_sysfs_mutex);
+ return retval;
}
static ssize_t fwu_sysfs_image_name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ ssize_t retval;
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
if (strnlen(fwu->rmi4_data->fw_image_name, NAME_BUFFER_SIZE) > 0)
- return snprintf(buf, PAGE_SIZE, "%s\n",
+ retval = snprintf(buf, PAGE_SIZE, "%s\n",
fwu->rmi4_data->fw_image_name);
else
- return snprintf(buf, PAGE_SIZE, "No firmware name given\n");
+ retval = snprintf(buf, PAGE_SIZE, "No firmware name given\n");
+
+ mutex_unlock(&fwu_sysfs_mutex);
+ return retval;
}
static ssize_t fwu_sysfs_force_reflash_store(struct device *dev,
@@ -1794,14 +1827,17 @@ static ssize_t fwu_sysfs_force_reflash_store(struct device *dev,
unsigned int input;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
+
if (sscanf(buf, "%u", &input) != 1) {
retval = -EINVAL;
- goto exit;
+ goto force_reflash_store_exit;
}
if (input != 1) {
retval = -EINVAL;
- goto exit;
+ goto force_reflash_store_exit;
}
if (LOCKDOWN)
fwu->do_lockdown = true;
@@ -1812,16 +1848,18 @@ static ssize_t fwu_sysfs_force_reflash_store(struct device *dev,
dev_err(&rmi4_data->i2c_client->dev,
"%s: Failed to do reflash\n",
__func__);
- goto exit;
+ goto force_reflash_store_free_exit;
}
retval = count;
-exit:
+force_reflash_store_free_exit:
kfree(fwu->ext_data_source);
fwu->ext_data_source = NULL;
fwu->force_update = FORCE_UPDATE;
fwu->do_lockdown = rmi4_data->board->do_lockdown;
+force_reflash_store_exit:
+ mutex_unlock(&fwu_sysfs_mutex);
return retval;
}
@@ -1832,9 +1870,12 @@ static ssize_t fwu_sysfs_do_reflash_store(struct device *dev,
unsigned int input;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
+
if (sscanf(buf, "%u", &input) != 1) {
retval = -EINVAL;
- goto exit;
+ goto reflash_store_exit;
}
if (input & LOCKDOWN) {
@@ -1844,7 +1885,7 @@ static ssize_t fwu_sysfs_do_reflash_store(struct device *dev,
if ((input != NORMAL) && (input != FORCE)) {
retval = -EINVAL;
- goto exit;
+ goto reflash_store_exit;
}
if (input == FORCE)
@@ -1855,16 +1896,18 @@ static ssize_t fwu_sysfs_do_reflash_store(struct device *dev,
dev_err(&rmi4_data->i2c_client->dev,
"%s: Failed to do reflash\n",
__func__);
- goto exit;
+ goto reflash_store_free_exit;
}
retval = count;
-exit:
+reflash_store_free_exit:
kfree(fwu->ext_data_source);
fwu->ext_data_source = NULL;
fwu->force_update = FORCE_UPDATE;
fwu->do_lockdown = rmi4_data->board->do_lockdown;
+reflash_store_exit:
+ mutex_unlock(&fwu_sysfs_mutex);
return retval;
}
@@ -1875,26 +1918,31 @@ static ssize_t fwu_sysfs_write_lockdown_store(struct device *dev,
unsigned int input;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
+
if (sscanf(buf, "%u", &input) != 1) {
retval = -EINVAL;
- goto exit;
+ goto lockdown_store_exit;
}
if (input != 1) {
retval = -EINVAL;
- goto exit;
+ goto lockdown_store_exit;
}
if (!fwu->ext_data_source) {
dev_err(&fwu->rmi4_data->i2c_client->dev,
"Cannot use this without loading image in manual way!\n");
- return -EAGAIN;
+ retval = -EAGAIN;
+ goto lockdown_store_exit;
}
if (fwu->rmi4_data->suspended == true) {
dev_err(&fwu->rmi4_data->i2c_client->dev,
"Cannot lockdown while device is in suspend\n");
- return -EBUSY;
+ retval = -EBUSY;
+ goto lockdown_store_exit;
}
retval = fwu_start_write_lockdown();
@@ -1902,16 +1950,18 @@ static ssize_t fwu_sysfs_write_lockdown_store(struct device *dev,
dev_err(&rmi4_data->i2c_client->dev,
"%s: Failed to write lockdown block\n",
__func__);
- goto exit;
+ goto lockdown_store_free_exit;
}
retval = count;
-exit:
+lockdown_store_free_exit:
kfree(fwu->ext_data_source);
fwu->ext_data_source = NULL;
fwu->force_update = FORCE_UPDATE;
fwu->do_lockdown = rmi4_data->board->do_lockdown;
+lockdown_store_exit:
+ mutex_unlock(&fwu_sysfs_mutex);
return retval;
}
@@ -1920,6 +1970,8 @@ static ssize_t fwu_sysfs_check_fw_store(struct device *dev,
{
unsigned int input = 0;
+ /* Takes fwu_sysfs_mutex in the deferred work function. */
+
if (sscanf(buf, "%u", &input) != 1)
return -EINVAL;
@@ -1942,26 +1994,31 @@ static ssize_t fwu_sysfs_write_config_store(struct device *dev,
unsigned int input;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
+
if (sscanf(buf, "%u", &input) != 1) {
retval = -EINVAL;
- goto exit;
+ goto write_config_store_exit;
}
if (input != 1) {
retval = -EINVAL;
- goto exit;
+ goto write_config_store_exit;
}
if (!fwu->ext_data_source) {
dev_err(&fwu->rmi4_data->i2c_client->dev,
"Cannot use this without loading image in manual way!\n");
- return -EAGAIN;
+ retval = -EAGAIN;
+ goto write_config_store_exit;
}
if (fwu->rmi4_data->suspended == true) {
dev_err(&fwu->rmi4_data->i2c_client->dev,
"Cannot write config while device is in suspend\n");
- return -EBUSY;
+ retval = -EBUSY;
+ goto write_config_store_exit;
}
retval = fwu_start_write_config();
@@ -1969,14 +2026,16 @@ static ssize_t fwu_sysfs_write_config_store(struct device *dev,
dev_err(&rmi4_data->i2c_client->dev,
"%s: Failed to write config\n",
__func__);
- goto exit;
+ goto write_config_store_free_exit;
}
retval = count;
-exit:
+write_config_store_free_exit:
kfree(fwu->ext_data_source);
fwu->ext_data_source = NULL;
+write_config_store_exit:
+ mutex_unlock(&fwu_sysfs_mutex);
return retval;
}
@@ -1999,7 +2058,11 @@ static ssize_t fwu_sysfs_read_config_store(struct device *dev,
return -EBUSY;
}
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
retval = fwu_do_read_config();
+ mutex_unlock(&fwu_sysfs_mutex);
+
if (retval < 0) {
dev_err(&rmi4_data->i2c_client->dev,
"%s: Failed to read config\n",
@@ -2028,7 +2091,10 @@ static ssize_t fwu_sysfs_config_area_store(struct device *dev,
return -EINVAL;
}
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
fwu->config_area = config_area;
+ mutex_unlock(&fwu_sysfs_mutex);
return count;
}
@@ -2039,10 +2105,12 @@ static ssize_t fwu_sysfs_image_size_store(struct device *dev,
int retval;
unsigned long size;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
retval = kstrtoul(buf, 10, &size);
if (retval)
- return retval;
+ goto image_size_store_exit;
fwu->image_size = size;
fwu->data_pos = 0;
@@ -2053,10 +2121,12 @@ static ssize_t fwu_sysfs_image_size_store(struct device *dev,
dev_err(&rmi4_data->i2c_client->dev,
"%s: Failed to alloc mem for image data\n",
__func__);
- return -ENOMEM;
+ retval = -ENOMEM;
}
- return count;
+image_size_store_exit:
+ mutex_unlock(&fwu_sysfs_mutex);
+ return retval;
}
static ssize_t fwu_sysfs_block_size_show(struct device *dev,
@@ -2241,6 +2311,8 @@ static void synaptics_rmi4_fwu_work(struct work_struct *work)
container_of(to_delayed_work(work),
struct synaptics_rmi4_fwu_handle, fwu_work);
+ mutex_lock(&fwu_sysfs_mutex);
+
if (fwu->fn_ptr->enable)
fwu->fn_ptr->enable(fwu->rmi4_data, false);
@@ -2248,6 +2320,8 @@ static void synaptics_rmi4_fwu_work(struct work_struct *work)
if (fwu->fn_ptr->enable)
fwu->fn_ptr->enable(fwu->rmi4_data, true);
+
+ mutex_unlock(&fwu_sysfs_mutex);
}
static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
@@ -2338,7 +2412,7 @@ static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
INIT_DELAYED_WORK(&fwu->fwu_work, synaptics_rmi4_fwu_work);
#endif
- retval = sysfs_create_bin_file(&rmi4_data->i2c_client->dev.kobj,
+ retval = sysfs_create_bin_file(&rmi4_data->i2c_client->dev.kobj,
&dev_attr_data);
if (retval < 0) {
dev_err(&rmi4_data->i2c_client->dev,