Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e3eb0147 authored by Andrew Chant's avatar Andrew Chant Committed by Srinivasa Rao Kuppala
Browse files

Input: synaptics: check input, 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.

Also check that ext_data_source is appropriately sized
and allocated, based on a patch by
Gengjia Chen (chengjia4574@gmail.com).

Signed-off-by: default avatarAndrew Chant <achant@google.com>
Change-Id:I5bbe4992f3fd2d23db288296eaeb61f5831098e9
Bug: 30799828
Bug: 31252388
Git-repo: https://android.googlesource.com/kernel/msm.git


Git-commit: 287ce2ccfefe68067c1f9f5175b6664bf7397fe6
Signed-off-by: default avatarSrinivasa Rao Kuppala <srkupp@codeaurora.org>
parent 4fa286af
Loading
Loading
Loading
Loading
+86 −10
Original line number Diff line number Diff line
@@ -358,6 +358,7 @@ static struct device_attribute attrs[] = {
static struct synaptics_rmi4_fwu_handle *fwu;

DECLARE_COMPLETION(fwu_dsx_remove_complete);
DEFINE_MUTEX(dsx_fwu_sysfs_mutex);

static unsigned int extract_uint_le(const unsigned char *ptr)
{
@@ -1589,39 +1590,72 @@ 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(&dsx_fwu_sysfs_mutex))
		return -EBUSY;

	if (count < fwu->config_size) {
		dev_err(rmi4_data->pdev->dev.parent,
				"%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(&dsx_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(&dsx_fwu_sysfs_mutex))
		return -EBUSY;

	if (count > (fwu->image_size - fwu->data_pos)) {
		dev_err(fwu->rmi4_data->pdev->dev.parent,
				"%s: Not enough space in buffer\n",
				__func__);
		retval = -EINVAL;
		goto exit;
	}

	if (!fwu->ext_data_source) {
		dev_err(fwu->rmi4_data->pdev->dev.parent,
				"%s: Need to set imagesize\n",
				__func__);
		retval = -EINVAL;
		goto exit;
	}

	memcpy((void *)(&fwu->ext_data_source[fwu->data_pos]),
			(const void *)buf,
			count);

	fwu->data_pos += count;

exit:
	mutex_unlock(&dsx_fwu_sysfs_mutex);
	return count;
}

static ssize_t fwu_sysfs_force_reflash_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	int retval;
	ssize_t retval;
	unsigned int input;
	struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;

	if (!mutex_trylock(&dsx_fwu_sysfs_mutex))
		return -EBUSY;

	if (sscanf(buf, "%u", &input) != 1) {
		retval = -EINVAL;
		goto exit;
@@ -1650,6 +1684,9 @@ exit:
	fwu->ext_data_source = NULL;
	fwu->force_update = FORCE_UPDATE;
	fwu->do_lockdown = DO_LOCKDOWN;
	fwu->data_pos = 0;
	fwu->image_size = 0;
	mutex_unlock(&dsx_fwu_sysfs_mutex);
	return retval;
}

@@ -1660,6 +1697,9 @@ 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(&dsx_fwu_sysfs_mutex))
		return -EBUSY;

	if (sscanf(buf, "%u", &input) != 1) {
		retval = -EINVAL;
		goto exit;
@@ -1693,6 +1733,9 @@ exit:
	fwu->ext_data_source = NULL;
	fwu->force_update = FORCE_UPDATE;
	fwu->do_lockdown = DO_LOCKDOWN;
	fwu->data_pos = 0;
	fwu->image_size = 0;
	mutex_unlock(&dsx_fwu_sysfs_mutex);
	return retval;
}

@@ -1703,6 +1746,9 @@ 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(&dsx_fwu_sysfs_mutex))
		return -EBUSY;

	if (sscanf(buf, "%u", &input) != 1) {
		retval = -EINVAL;
		goto exit;
@@ -1726,6 +1772,9 @@ static ssize_t fwu_sysfs_write_config_store(struct device *dev,
exit:
	kfree(fwu->ext_data_source);
	fwu->ext_data_source = NULL;
	fwu->data_pos = 0;
	fwu->image_size = 0;
	mutex_unlock(&dsx_fwu_sysfs_mutex);
	return retval;
}

@@ -1742,7 +1791,11 @@ static ssize_t fwu_sysfs_read_config_store(struct device *dev,
	if (input != 1)
		return -EINVAL;

	if (!mutex_trylock(&dsx_fwu_sysfs_mutex))
		return -EBUSY;
	retval = fwu_do_read_config();
	mutex_unlock(&dsx_fwu_sysfs_mutex);

	if (retval < 0) {
		dev_err(rmi4_data->pdev->dev.parent,
				"%s: Failed to read config\n",
@@ -1763,7 +1816,10 @@ static ssize_t fwu_sysfs_config_area_store(struct device *dev,
	if (retval)
		return retval;

	if (!mutex_trylock(&dsx_fwu_sysfs_mutex))
		return -EBUSY;
	fwu->config_area = config_area;
	mutex_unlock(&dsx_fwu_sysfs_mutex);

	return count;
}
@@ -1771,17 +1827,30 @@ static ssize_t fwu_sysfs_config_area_store(struct device *dev,
static ssize_t fwu_sysfs_image_name_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	ssize_t retval;

	if (!mutex_trylock(&dsx_fwu_sysfs_mutex))
		return -EBUSY;
	if (strnlen(fwu->rmi4_data->fw_name, SYNA_FW_NAME_MAX_LEN) > 0)
		return snprintf(buf, PAGE_SIZE, "%s\n",
		retval = snprintf(buf, PAGE_SIZE, "%s\n",
					fwu->rmi4_data->fw_name);
	else
		return snprintf(buf, PAGE_SIZE, "No firmware name given\n");
		retval = snprintf(buf, PAGE_SIZE, "No firmware name given\n");
	mutex_unlock(&dsx_fwu_sysfs_mutex);
	return retval;
}

static ssize_t fwu_sysfs_image_name_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	if (sscanf(buf, "%s", fwu->image_name) != 1)
	ssize_t retval;

	if (!mutex_trylock(&dsx_fwu_sysfs_mutex))
		return -EBUSY;
	retval = sscanf(buf, "%49s", fwu->image_name);
	mutex_unlock(&dsx_fwu_sysfs_mutex);

	if (retval != 1)
		return -EINVAL;

	return count;
@@ -1794,9 +1863,12 @@ static ssize_t fwu_sysfs_image_size_store(struct device *dev,
	unsigned long size;
	struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;

	if (!mutex_trylock(&dsx_fwu_sysfs_mutex))
		return -EBUSY;

	retval = sstrtoul(buf, 10, &size);
	if (retval)
		return retval;
		goto exit;

	fwu->image_size = size;
	fwu->data_pos = 0;
@@ -1807,10 +1879,14 @@ static ssize_t fwu_sysfs_image_size_store(struct device *dev,
		dev_err(rmi4_data->pdev->dev.parent,
				"%s: Failed to alloc mem for image data\n",
				__func__);
		return -ENOMEM;
		retval = -ENOMEM;
		goto exit;
	}

	return count;
	retval = count;
exit:
	mutex_unlock(&dsx_fwu_sysfs_mutex);
	return retval;
}

static ssize_t fwu_sysfs_block_size_show(struct device *dev,
+86 −11
Original line number Diff line number Diff line
@@ -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)
{
@@ -1680,28 +1682,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");
		retval = -EAGAIN;
		goto 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 exit;
	}

	memcpy((void *)(&fwu->ext_data_source[fwu->data_pos]),
@@ -1710,8 +1731,10 @@ static ssize_t fwu_sysfs_store_image(struct file *data_file,

	fwu->data_buffer = fwu->ext_data_source;
	fwu->data_pos += count;

	return count;
	retval = count;
exit:
	mutex_unlock(&fwu_sysfs_mutex);
	return retval;
}

static ssize_t fwu_sysfs_image_name_store(struct device *dev,
@@ -1734,18 +1757,29 @@ static ssize_t fwu_sysfs_image_name_store(struct device *dev,
		return -EINVAL;
	}

	if (!mutex_trylock(&fwu_sysfs_mutex))
		return -EBUSY;
	strlcpy(rmi4_data->fw_image_name, buf, count);
	mutex_unlock(&fwu_sysfs_mutex);

	return count;
}

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,
@@ -1755,6 +1789,9 @@ 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;

	retval = kstrtouint(buf, 10, &input);
	if (retval)
		goto exit;
@@ -1781,7 +1818,10 @@ exit:
	kfree(fwu->ext_data_source);
	fwu->ext_data_source = NULL;
	fwu->force_update = FORCE_UPDATE;
	fwu->image_size = 0;
	fwu->data_pos = 0;
	fwu->do_lockdown = rmi4_data->board->do_lockdown;
	mutex_unlock(&fwu_sysfs_mutex);
	return retval;
}

@@ -1792,6 +1832,9 @@ 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;

	retval = kstrtouint(buf, 10, &input);
	if (retval)
		goto exit;
@@ -1822,8 +1865,11 @@ static ssize_t fwu_sysfs_do_reflash_store(struct device *dev,
exit:
	kfree(fwu->ext_data_source);
	fwu->ext_data_source = NULL;
	fwu->image_size = 0;
	fwu->data_pos = 0;
	fwu->force_update = FORCE_UPDATE;
	fwu->do_lockdown = rmi4_data->board->do_lockdown;
	mutex_unlock(&fwu_sysfs_mutex);
	return retval;
}

@@ -1834,6 +1880,9 @@ 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;
@@ -1859,6 +1908,9 @@ exit:
	fwu->ext_data_source = NULL;
	fwu->force_update = FORCE_UPDATE;
	fwu->do_lockdown = rmi4_data->board->do_lockdown;
	fwu->image_size = 0;
	fwu->data_pos = 0;
	mutex_unlock(&fwu_sysfs_mutex);
	return retval;
}

@@ -1869,6 +1921,9 @@ 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;

	retval = kstrtouint(buf, 10, &input);
	if (retval)
		goto exit;
@@ -1891,6 +1946,9 @@ static ssize_t fwu_sysfs_write_config_store(struct device *dev,
exit:
	kfree(fwu->ext_data_source);
	fwu->ext_data_source = NULL;
	fwu->image_size = 0;
	fwu->data_pos = 0;
	mutex_unlock(&fwu_sysfs_mutex);
	return retval;
}

@@ -1908,7 +1966,11 @@ static ssize_t fwu_sysfs_read_config_store(struct device *dev,
	if (input != 1)
		return -EINVAL;

	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",
@@ -1937,7 +1999,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;
}
@@ -1952,15 +2017,23 @@ static ssize_t fwu_sysfs_image_size_store(struct device *dev,
	if (retval)
		return retval;

	if (!mutex_trylock(&fwu_sysfs_mutex))
		return -EBUSY;

	fwu->image_size = size;
	fwu->data_pos = 0;

	kfree(fwu->ext_data_source);
	fwu->ext_data_source = kzalloc(fwu->image_size, GFP_KERNEL);
	if (!fwu->ext_data_source)
		return -ENOMEM;
	if (!fwu->ext_data_source) {
		retval = -ENOMEM;
		goto exit;
	}

	return count;
	retval = count;
exit:
	mutex_unlock(&fwu_sysfs_mutex);
	return retval;
}

static ssize_t fwu_sysfs_block_size_show(struct device *dev,
@@ -2124,7 +2197,9 @@ static struct device_attribute attrs[] = {

static void synaptics_rmi4_fwu_work(struct work_struct *work)
{
	mutex_lock(&fwu_sysfs_mutex);
	fwu_start_reflash();
	mutex_unlock(&fwu_sysfs_mutex);
}

static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)