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

Commit db2cdc95 authored by Ankit Sharma's avatar Ankit Sharma
Browse files

leds: qpnp-flash: Fix Use-after-free(UAF) for debugfs



Fix UAF where two threads can open and close the same file. Second
open will cause the private data for the first file to be overwritten.
When the first file is closed and the private data is freed, this makes
the now-shared private data OOB for the second thread.

CRs-Fixed: 1109763
Change-Id: I1c4618d5be99e140abf0f3ea0d7f485897db5ab2
Signed-off-by: default avatarAnkit Sharma <ansharma@codeaurora.org>
parent ae5da9c9
Loading
Loading
Loading
Loading
+54 −28
Original line number Diff line number Diff line
@@ -229,6 +229,8 @@ struct qpnp_flash_led_buffer {
	size_t		rpos;
	size_t		wpos;
	size_t		len;
	struct		qpnp_flash_led *led;
	u32		buffer_cnt;
	char		data[0];
};

@@ -247,10 +249,8 @@ struct qpnp_flash_led {
	struct workqueue_struct		*ordered_workq;
	struct qpnp_vadc_chip		*vadc_dev;
	struct mutex			flash_led_lock;
	struct qpnp_flash_led_buffer	*log;
	struct dentry			*dbgfs_root;
	int				num_leds;
	u32				buffer_cnt;
	u16				base;
	u16				current_addr;
	u16				current2_addr;
@@ -282,10 +282,10 @@ static int flash_led_dbgfs_file_open(struct qpnp_flash_led *led,
	log->wpos = 0;
	log->len = logbufsize - sizeof(*log);
	mutex_init(&log->debugfs_lock);
	led->log = log;
	log->led = led;

	led->buffer_cnt = 1;
	file->private_data = led;
	log->buffer_cnt = 1;
	file->private_data = log;

	return 0;
}
@@ -299,12 +299,12 @@ static int flash_led_dfs_open(struct inode *inode, struct file *file)

static int flash_led_dfs_close(struct inode *inode, struct file *file)
{
	struct qpnp_flash_led *led = file->private_data;
	struct qpnp_flash_led_buffer *log = file->private_data;

	if (led && led->log) {
	if (log) {
		file->private_data = NULL;
		mutex_destroy(&led->log->debugfs_lock);
		kfree(led->log);
		mutex_destroy(&log->debugfs_lock);
		kfree(log);
	}

	return 0;
@@ -333,15 +333,21 @@ static int print_to_log(struct qpnp_flash_led_buffer *log,

static ssize_t flash_led_dfs_latched_reg_read(struct file *fp, char __user *buf,
					size_t count, loff_t *ppos) {
	struct qpnp_flash_led *led = fp->private_data;
	struct qpnp_flash_led_buffer *log = led->log;
	struct qpnp_flash_led_buffer *log = fp->private_data;
	struct qpnp_flash_led *led;
	u8 val;
	int rc = 0;
	size_t len;
	size_t ret;

	if (!log) {
		pr_err("error: file private data is NULL\n");
		return -EFAULT;
	}
	led = log->led;

	mutex_lock(&log->debugfs_lock);
	if ((log->rpos >= log->wpos && led->buffer_cnt == 0) ||
	if ((log->rpos >= log->wpos && log->buffer_cnt == 0) ||
			((log->len - log->wpos) < MIN_BUFFER_WRITE_LEN))
		goto unlock_mutex;

@@ -353,7 +359,7 @@ static ssize_t flash_led_dfs_latched_reg_read(struct file *fp, char __user *buf,
				INT_LATCHED_STS(led->base), rc);
		goto unlock_mutex;
	}
	led->buffer_cnt--;
	log->buffer_cnt--;

	rc = print_to_log(log, "0x%05X ", INT_LATCHED_STS(led->base));
	if (rc == 0)
@@ -388,18 +394,24 @@ unlock_mutex:

static ssize_t flash_led_dfs_fault_reg_read(struct file *fp, char __user *buf,
					size_t count, loff_t *ppos) {
	struct qpnp_flash_led *led = fp->private_data;
	struct qpnp_flash_led_buffer *log = led->log;
	struct qpnp_flash_led_buffer *log = fp->private_data;
	struct qpnp_flash_led *led;
	int rc = 0;
	size_t len;
	size_t ret;

	if (!log) {
		pr_err("error: file private data is NULL\n");
		return -EFAULT;
	}
	led = log->led;

	mutex_lock(&log->debugfs_lock);
	if ((log->rpos >= log->wpos && led->buffer_cnt == 0) ||
	if ((log->rpos >= log->wpos && log->buffer_cnt == 0) ||
			((log->len - log->wpos) < MIN_BUFFER_WRITE_LEN))
		goto unlock_mutex;

	led->buffer_cnt--;
	log->buffer_cnt--;

	rc = print_to_log(log, "0x%05X ", FLASH_LED_FAULT_STATUS(led->base));
	if (rc == 0)
@@ -441,10 +453,17 @@ static ssize_t flash_led_dfs_fault_reg_enable(struct file *file,
	int data;
	size_t ret = 0;

	struct qpnp_flash_led *led = file->private_data;
	struct qpnp_flash_led_buffer *log = file->private_data;
	struct qpnp_flash_led *led;
	char *kbuf;

	mutex_lock(&led->log->debugfs_lock);
	if (!log) {
		pr_err("error: file private data is NULL\n");
		return -EFAULT;
	}
	led = log->led;

	mutex_lock(&log->debugfs_lock);
	kbuf = kmalloc(count + 1, GFP_KERNEL);
	if (!kbuf) {
		ret = -ENOMEM;
@@ -479,7 +498,7 @@ static ssize_t flash_led_dfs_fault_reg_enable(struct file *file,
free_buf:
	kfree(kbuf);
unlock_mutex:
	mutex_unlock(&led->log->debugfs_lock);
	mutex_unlock(&log->debugfs_lock);
	return ret;
}

@@ -491,10 +510,17 @@ static ssize_t flash_led_dfs_dbg_enable(struct file *file,
	int cnt = 0;
	int data;
	size_t ret = 0;
	struct qpnp_flash_led *led = file->private_data;
	struct qpnp_flash_led_buffer *log = file->private_data;
	struct qpnp_flash_led *led;
	char *kbuf;

	mutex_lock(&led->log->debugfs_lock);
	if (!log) {
		pr_err("error: file private data is NULL\n");
		return -EFAULT;
	}
	led = log->led;

	mutex_lock(&log->debugfs_lock);
	kbuf = kmalloc(count + 1, GFP_KERNEL);
	if (!kbuf) {
		ret = -ENOMEM;
@@ -528,7 +554,7 @@ static ssize_t flash_led_dfs_dbg_enable(struct file *file,
free_buf:
	kfree(kbuf);
unlock_mutex:
	mutex_unlock(&led->log->debugfs_lock);
	mutex_unlock(&log->debugfs_lock);
	return ret;
}