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

Commit e72bd371 authored by Naresh Malladi's avatar Naresh Malladi
Browse files

drivers: soc: qcom: rpm_stats: Add mutex lock for shared data



The buffer allocated in file open operations need to be
protected as there can be a possiblity of use-after-free
scenario.

Process A              B
        |              |
      open             |
        |              |
      read started     |
        |             close

Add mutex lock to protect the buffer to avoid this.

"msm_rpmstats_copy_stats" accesses the variable "pdata->read_idx"
without locking. The userspace can invoke the "read" call from
multiple threads which will call "msm_rpmstats_file_read" which
in turn calls "msm_rpmstats_copy_stats".

This can allow the statement "pdata->read_idx++" increment
"read_idx" beyond the limit ("prvdata->num_records") and call
"msm_rpmstats_read_register" with this value.

Also allow reading RPM stats information using sysfs nodes.

The stats are available at
	/sys/power/system_sleep/stats

Change-Id: I031f02bb2694a97ced86da0a9f54d0e434e4ad6d
Signed-off-by: default avatarNaresh Malladi <namall@codeaurora.org>
parent f4903509
Loading
Loading
Loading
Loading
+34 −16
Original line number Diff line number Diff line
@@ -50,6 +50,8 @@

#define GET_FIELD(a) ((strnstr(#a, ".", 80) + 1))

static DEFINE_MUTEX(msm_rpm_master_stats_mutex);

struct msm_rpm_master_stats {
	uint32_t active_cores;
	uint32_t numshutdowns;
@@ -80,9 +82,11 @@ int msm_rpm_master_stats_file_close(struct inode *inode,
{
	struct msm_rpm_master_stats_private_data *private = file->private_data;

	mutex_lock(&msm_rpm_master_stats_mutex);
	if (private->reg_base)
		iounmap(private->reg_base);
	kfree(file->private_data);
	mutex_unlock(&msm_rpm_master_stats_mutex);

	return 0;
}
@@ -95,15 +99,11 @@ static int msm_rpm_master_copy_stats(
	static int master_cnt;
	int count, j = 0;
	char *buf;
	static DEFINE_MUTEX(msm_rpm_master_stats_mutex);
	unsigned long active_cores;

	mutex_lock(&msm_rpm_master_stats_mutex);

	/* Iterate possible number of masters */
	if (master_cnt > prvdata->num_masters - 1) {
		master_cnt = 0;
		mutex_unlock(&msm_rpm_master_stats_mutex);
		return 0;
	}

@@ -256,7 +256,6 @@ static int msm_rpm_master_copy_stats(
	}

	master_cnt++;
	mutex_unlock(&msm_rpm_master_stats_mutex);
	return RPM_MASTERS_BUF_LEN - count;
}

@@ -265,25 +264,36 @@ static ssize_t msm_rpm_master_stats_file_read(struct file *file,
{
	struct msm_rpm_master_stats_private_data *prvdata;
	struct msm_rpm_master_stats_platform_data *pdata;
	ssize_t ret;

	mutex_lock(&msm_rpm_master_stats_mutex);
	prvdata = file->private_data;
	if (!prvdata)
		return -EINVAL;
	if (!prvdata) {
		ret = -EINVAL;
		goto exit;
	}

	pdata = prvdata->platform_data;
	if (!pdata)
		return -EINVAL;
	if (!pdata) {
		ret = -EINVAL;
		goto exit;
	}

	if (!bufu || count == 0)
		return -EINVAL;
	if (!bufu || count == 0) {
		ret = -EINVAL;
		goto exit;
	}

	if ((*ppos <= pdata->phys_size)) {
		prvdata->len = msm_rpm_master_copy_stats(prvdata);
		*ppos = 0;
	}

	return simple_read_from_buffer(bufu, count, ppos,
	ret = simple_read_from_buffer(bufu, count, ppos,
			prvdata->buf, prvdata->len);
exit:
	mutex_unlock(&msm_rpm_master_stats_mutex);
	return ret;
}

static int msm_rpm_master_stats_file_open(struct inode *inode,
@@ -291,15 +301,20 @@ static int msm_rpm_master_stats_file_open(struct inode *inode,
{
	struct msm_rpm_master_stats_private_data *prvdata;
	struct msm_rpm_master_stats_platform_data *pdata;
	int ret = 0;

	mutex_lock(&msm_rpm_master_stats_mutex);
	pdata = inode->i_private;

	file->private_data =
		kzalloc(sizeof(struct msm_rpm_master_stats_private_data),
			GFP_KERNEL);

	if (!file->private_data)
		return -ENOMEM;
	if (!file->private_data) {
		ret = -ENOMEM;
		goto exit;
	}

	prvdata = file->private_data;

	prvdata->reg_base = ioremap(pdata->phys_addr_base,
@@ -310,14 +325,17 @@ static int msm_rpm_master_stats_file_open(struct inode *inode,
		pr_err("%s: ERROR could not ioremap start=%pa, len=%u\n",
			__func__, &pdata->phys_addr_base,
			pdata->phys_size);
		return -EBUSY;
		ret = -EBUSY;
		goto exit;
	}

	prvdata->len = 0;
	prvdata->num_masters = pdata->num_masters;
	prvdata->master_names = pdata->masters;
	prvdata->platform_data = pdata;
	return 0;
exit:
	mutex_unlock(&msm_rpm_master_stats_mutex);
	return ret;
}

static const struct file_operations msm_rpm_master_stats_fops = {
+39 −14
Original line number Diff line number Diff line
@@ -46,6 +46,8 @@

#define NAMELEN (sizeof(uint32_t)+1)

static DEFINE_MUTEX(msm_rpm_rail_stats_mutex);

struct msm_rpm_rail_stats_platform_data {
	phys_addr_t phys_addr_base;
	u32 phys_size;
@@ -80,9 +82,11 @@ int msm_rpm_rail_stats_file_close(struct inode *inode, struct file *file)
{
	struct msm_rpm_rail_stats_private_data *private = file->private_data;

	mutex_lock(&msm_rpm_rail_stats_mutex);
	if (private->reg_base)
		iounmap(private->reg_base);
	kfree(file->private_data);
	mutex_unlock(&msm_rpm_rail_stats_mutex);

	return 0;
}
@@ -154,18 +158,26 @@ static int msm_rpm_rail_stats_copy(
static ssize_t msm_rpm_rail_stats_file_read(struct file *file,
				char __user *bufu, size_t count, loff_t *ppos)
{
	struct msm_rpm_rail_stats_private_data *prvdata =
		file->private_data;
	struct msm_rpm_rail_stats_private_data *prvdata;
	struct msm_rpm_rail_stats_platform_data *pdata;
	ssize_t ret;

	if (!prvdata)
		return -EINVAL;
	mutex_lock(&msm_rpm_rail_stats_mutex);
	prvdata = file->private_data;
	if (!prvdata) {
		ret = -EINVAL;
		goto exit;
	}

	if (!prvdata->platform_data)
		return -EINVAL;
	if (!prvdata->platform_data) {
		ret = -EINVAL;
		goto exit;
	}

	if (!bufu || count == 0)
		return -EINVAL;
	if (!bufu || count == 0) {
		ret = -EINVAL;
		goto exit;
	}

	pdata = prvdata->platform_data;

@@ -174,22 +186,32 @@ static ssize_t msm_rpm_rail_stats_file_read(struct file *file,
		*ppos = 0;
	}

	return simple_read_from_buffer(bufu, count, ppos,
	ret = simple_read_from_buffer(bufu, count, ppos,
			prvdata->buf, prvdata->len);
exit:
	mutex_unlock(&msm_rpm_rail_stats_mutex);
	return ret;
}

static int msm_rpm_rail_stats_file_open(struct inode *inode,
		struct file *file)
{
	struct msm_rpm_rail_stats_private_data *prvdata;
	struct msm_rpm_rail_stats_platform_data *pdata = inode->i_private;
	struct msm_rpm_rail_stats_platform_data *pdata;
	int ret = 0;

	mutex_lock(&msm_rpm_rail_stats_mutex);
	pdata = inode->i_private;

	file->private_data =
		kzalloc(sizeof(struct msm_rpm_rail_stats_private_data),
			GFP_KERNEL);

	if (!file->private_data)
		return -ENOMEM;
	if (!file->private_data) {
		ret = -ENOMEM;
		goto exit;
	}

	prvdata = file->private_data;

	prvdata->reg_base = ioremap(pdata->phys_addr_base,
@@ -200,12 +222,15 @@ static int msm_rpm_rail_stats_file_open(struct inode *inode,
		pr_err("%s: ERROR could not ioremap start=%pa, len=%u\n",
			__func__, &pdata->phys_addr_base,
			pdata->phys_size);
		return -EBUSY;
		ret = -EBUSY;
		goto exit;
	}

	prvdata->len = 0;
	prvdata->platform_data = pdata;
	return 0;
exit:
	mutex_unlock(&msm_rpm_rail_stats_mutex);
	return ret;
}


+53 −23
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@
#define GET_PDATA_OF_ATTR(attr) \
	(container_of(attr, struct msm_rpmstats_kobj_attr, ka)->pd)

static DEFINE_MUTEX(rpm_stats_mutex);

enum {
	ID_COUNTER,
	ID_ACCUM_TIME_SCLK,
@@ -218,6 +220,12 @@ static int msm_rpmstats_copy_stats(struct msm_rpmstats_private_data *pdata)

	record.id = msm_rpmstats_read_register(pdata->reg_base,
						pdata->read_idx, 1);
	if (record.id >= ID_MAX) {
		pr_err("%s: array out of bound error found.\n",
			__func__);
		return -EINVAL;
	}

	record.val = msm_rpmstats_read_register(pdata->reg_base,
						pdata->read_idx, 2);

@@ -240,13 +248,20 @@ static ssize_t msm_rpmstats_file_read(struct file *file, char __user *bufu,
				  size_t count, loff_t *ppos)
{
	struct msm_rpmstats_private_data *prvdata;
	ssize_t ret;

	mutex_lock(&rpm_stats_mutex);
	prvdata = file->private_data;

	if (!prvdata)
		return -EINVAL;
	if (!prvdata) {
		ret = -EINVAL;
		goto exit;
	}

	if (!bufu || count == 0)
		return -EINVAL;
	if (!bufu || count == 0) {
		ret = -EINVAL;
		goto exit;
	}

	if (prvdata->platform_data->version == 1) {
		if (!prvdata->num_records)
@@ -263,22 +278,30 @@ static ssize_t msm_rpmstats_file_read(struct file *file, char __user *bufu,
			*ppos = 0;
	}

	return simple_read_from_buffer(bufu, count, ppos,
	ret = simple_read_from_buffer(bufu, count, ppos,
			prvdata->buf, prvdata->len);
exit:
	mutex_unlock(&rpm_stats_mutex);
	return ret;
}

static int msm_rpmstats_file_open(struct inode *inode, struct file *file)
{
	struct msm_rpmstats_private_data *prvdata;
	struct msm_rpmstats_platform_data *pdata;
	int ret = 0;

	mutex_lock(&rpm_stats_mutex);
	pdata = inode->i_private;

	file->private_data =
		kmalloc(sizeof(struct msm_rpmstats_private_data), GFP_KERNEL);

	if (!file->private_data)
		return -ENOMEM;
	if (!file->private_data) {
		ret = -ENOMEM;
		goto exit;
	}

	prvdata = file->private_data;

	prvdata->reg_base = ioremap_nocache(pdata->phys_addr_base,
@@ -289,24 +312,28 @@ static int msm_rpmstats_file_open(struct inode *inode, struct file *file)
		pr_err("%s: ERROR could not ioremap start=%pa, len=%u\n",
			__func__, &pdata->phys_addr_base,
			pdata->phys_size);
		return -EBUSY;
		ret = -EBUSY;
		goto exit;
	}

	prvdata->read_idx = prvdata->num_records =  prvdata->len = 0;
	prvdata->platform_data = pdata;
	if (pdata->version == 2)
		prvdata->num_records = 2;

	return 0;
exit:
	mutex_unlock(&rpm_stats_mutex);
	return ret;
}

static int msm_rpmstats_file_close(struct inode *inode, struct file *file)
{
	struct msm_rpmstats_private_data *private = file->private_data;

	mutex_lock(&rpm_stats_mutex);
	if (private->reg_base)
		iounmap(private->reg_base);
	kfree(file->private_data);
	mutex_unlock(&rpm_stats_mutex);

	return 0;
}
@@ -362,22 +389,26 @@ static ssize_t rpmstats_show(struct kobject *kobj,
{
	struct msm_rpmstats_private_data *prvdata = NULL;
	struct msm_rpmstats_platform_data *pdata = NULL;
	ssize_t ret;

	mutex_lock(&rpm_stats_mutex);
	pdata = GET_PDATA_OF_ATTR(attr);

	prvdata =
		kmalloc(sizeof(*prvdata), GFP_KERNEL);
	if (!prvdata)
		return -ENOMEM;
	if (!prvdata) {
		ret = -ENOMEM;
		goto kmalloc_fail;
	}

	prvdata->reg_base = ioremap_nocache(pdata->phys_addr_base,
					pdata->phys_size);
	if (!prvdata->reg_base) {
		kfree(prvdata);
		pr_err("%s: ERROR could not ioremap start=%pa, len=%u\n",
			__func__, &pdata->phys_addr_base,
			pdata->phys_size);
		return -EBUSY;
		ret = -EBUSY;
		goto ioremap_fail;
	}

	prvdata->read_idx = prvdata->num_records =  prvdata->len = 0;
@@ -399,23 +430,22 @@ static ssize_t rpmstats_show(struct kobject *kobj,
					prvdata);
	}

	return snprintf(buf, prvdata->len, prvdata->buf);
	ret = snprintf(buf, prvdata->len, prvdata->buf);
	iounmap(prvdata->reg_base);
ioremap_fail:
	kfree(prvdata);
kmalloc_fail:
	mutex_unlock(&rpm_stats_mutex);
	return ret;
}

static int msm_rpmstats_create_sysfs(struct msm_rpmstats_platform_data *pd)
{
	struct kobject *module_kobj = NULL;
	struct kobject *rpmstats_kobj = NULL;
	struct msm_rpmstats_kobj_attr *rpms_ka = NULL;
	int ret = 0;

	module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
	if (!module_kobj) {
		pr_err("%s: Cannot find module_kset\n", __func__);
		return -ENODEV;
	}

	rpmstats_kobj = kobject_create_and_add("rpmstats", module_kobj);
	rpmstats_kobj = kobject_create_and_add("system_sleep", power_kobj);
	if (!rpmstats_kobj) {
		pr_err("%s: Cannot create rpmstats kobject\n", __func__);
		ret = -ENOMEM;