mirror of
https://github.com/Divested-Mobile/DivestOS-Build.git
synced 2024-12-20 21:34:24 -05:00
192 lines
5.7 KiB
Diff
192 lines
5.7 KiB
Diff
|
From 49b9a02eaaeb0b70608c6fbcadff7d83833b9614 Mon Sep 17 00:00:00 2001
|
||
|
From: Sarada Prasanna Garnayak <sgarna@codeaurora.org>
|
||
|
Date: Mon, 17 Apr 2017 14:29:57 +0530
|
||
|
Subject: wcnss: fix the potential memory leak and heap overflow
|
||
|
|
||
|
The wcnss platform driver update the wlan calibration data
|
||
|
by the user space wlan daemon. The wlan user space daemon store
|
||
|
the updated wlan calibration data reported by wlan firmware in
|
||
|
user space and write it back to the wcnss platform calibration
|
||
|
data buffer for the calibration data download and update.
|
||
|
|
||
|
During the wlan calibration data store and retrieve operation
|
||
|
there are some potential race condition which leads to memory leak
|
||
|
and buffer overflow during the context switch.
|
||
|
|
||
|
Fix the above issue by adding protection code and avoid usage of
|
||
|
global pointer during the device file read and write operation.
|
||
|
|
||
|
CRs-Fixed: 2015858
|
||
|
Change-Id: Ib5b57eb86dcb4e6ed799b5222d06396eaabfaad3
|
||
|
Signed-off-by: Sarada Prasanna Garnayak <sgarna@codeaurora.org>
|
||
|
---
|
||
|
drivers/net/wireless/wcnss/wcnss_wlan.c | 87 +++++++++++++++++++--------------
|
||
|
1 file changed, 51 insertions(+), 36 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
|
||
|
index 11ba537..3171a40 100644
|
||
|
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
|
||
|
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
|
||
|
@@ -396,7 +396,6 @@ static struct {
|
||
|
int user_cal_available;
|
||
|
u32 user_cal_rcvd;
|
||
|
int user_cal_exp_size;
|
||
|
- int device_opened;
|
||
|
int iris_xo_mode_set;
|
||
|
int fw_vbatt_state;
|
||
|
char wlan_nv_macAddr[WLAN_MAC_ADDR_SIZE];
|
||
|
@@ -3284,14 +3283,6 @@ static int wcnss_node_open(struct inode *inode, struct file *file)
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
|
||
|
- mutex_lock(&penv->dev_lock);
|
||
|
- penv->user_cal_rcvd = 0;
|
||
|
- penv->user_cal_read = 0;
|
||
|
- penv->user_cal_available = false;
|
||
|
- penv->user_cal_data = NULL;
|
||
|
- penv->device_opened = 1;
|
||
|
- mutex_unlock(&penv->dev_lock);
|
||
|
-
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
@@ -3300,7 +3291,7 @@ static ssize_t wcnss_wlan_read(struct file *fp, char __user
|
||
|
{
|
||
|
int rc = 0;
|
||
|
|
||
|
- if (!penv || !penv->device_opened)
|
||
|
+ if (!penv)
|
||
|
return -EFAULT;
|
||
|
|
||
|
rc = wait_event_interruptible(penv->read_wait, penv->fw_cal_rcvd
|
||
|
@@ -3337,55 +3328,66 @@ static ssize_t wcnss_wlan_write(struct file *fp, const char __user
|
||
|
*user_buffer, size_t count, loff_t *position)
|
||
|
{
|
||
|
int rc = 0;
|
||
|
- u32 size = 0;
|
||
|
+ char *cal_data = NULL;
|
||
|
|
||
|
- if (!penv || !penv->device_opened || penv->user_cal_available)
|
||
|
+ if (!penv || penv->user_cal_available)
|
||
|
return -EFAULT;
|
||
|
|
||
|
- if (penv->user_cal_rcvd == 0 && count >= 4
|
||
|
- && !penv->user_cal_data) {
|
||
|
- rc = copy_from_user((void *)&size, user_buffer, 4);
|
||
|
- if (!size || size > MAX_CALIBRATED_DATA_SIZE) {
|
||
|
- pr_err(DEVICE " invalid size to write %d\n", size);
|
||
|
+ if (!penv->user_cal_rcvd && count >= 4 && !penv->user_cal_exp_size) {
|
||
|
+ mutex_lock(&penv->dev_lock);
|
||
|
+ rc = copy_from_user((void *)&penv->user_cal_exp_size,
|
||
|
+ user_buffer, 4);
|
||
|
+ if (!penv->user_cal_exp_size ||
|
||
|
+ penv->user_cal_exp_size > MAX_CALIBRATED_DATA_SIZE) {
|
||
|
+ pr_err(DEVICE " invalid size to write %d\n",
|
||
|
+ penv->user_cal_exp_size);
|
||
|
+ penv->user_cal_exp_size = 0;
|
||
|
+ mutex_unlock(&penv->dev_lock);
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
-
|
||
|
- rc += count;
|
||
|
- count -= 4;
|
||
|
- penv->user_cal_exp_size = size;
|
||
|
- penv->user_cal_data = kmalloc(size, GFP_KERNEL);
|
||
|
- if (penv->user_cal_data == NULL) {
|
||
|
- pr_err(DEVICE " no memory to write\n");
|
||
|
- return -ENOMEM;
|
||
|
- }
|
||
|
- if (0 == count)
|
||
|
- goto exit;
|
||
|
-
|
||
|
- } else if (penv->user_cal_rcvd == 0 && count < 4)
|
||
|
+ mutex_unlock(&penv->dev_lock);
|
||
|
+ return count;
|
||
|
+ } else if (!penv->user_cal_rcvd && count < 4) {
|
||
|
return -EFAULT;
|
||
|
+ }
|
||
|
|
||
|
+ mutex_lock(&penv->dev_lock);
|
||
|
if ((UINT32_MAX - count < penv->user_cal_rcvd) ||
|
||
|
(penv->user_cal_exp_size < count + penv->user_cal_rcvd)) {
|
||
|
pr_err(DEVICE " invalid size to write %zu\n", count +
|
||
|
penv->user_cal_rcvd);
|
||
|
- rc = -ENOMEM;
|
||
|
- goto exit;
|
||
|
+ mutex_unlock(&penv->dev_lock);
|
||
|
+ return -ENOMEM;
|
||
|
}
|
||
|
- rc = copy_from_user((void *)penv->user_cal_data +
|
||
|
- penv->user_cal_rcvd, user_buffer, count);
|
||
|
- if (0 == rc) {
|
||
|
+
|
||
|
+ cal_data = kmalloc(count, GFP_KERNEL);
|
||
|
+ if (!cal_data) {
|
||
|
+ mutex_unlock(&penv->dev_lock);
|
||
|
+ return -ENOMEM;
|
||
|
+ }
|
||
|
+
|
||
|
+ rc = copy_from_user(cal_data, user_buffer, count);
|
||
|
+ if (!rc) {
|
||
|
+ memcpy(penv->user_cal_data + penv->user_cal_rcvd,
|
||
|
+ cal_data, count);
|
||
|
penv->user_cal_rcvd += count;
|
||
|
rc += count;
|
||
|
}
|
||
|
+
|
||
|
+ kfree(cal_data);
|
||
|
if (penv->user_cal_rcvd == penv->user_cal_exp_size) {
|
||
|
penv->user_cal_available = true;
|
||
|
pr_info_ratelimited("wcnss: user cal written");
|
||
|
}
|
||
|
+ mutex_unlock(&penv->dev_lock);
|
||
|
|
||
|
-exit:
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
+static int wcnss_node_release(struct inode *inode, struct file *file)
|
||
|
+{
|
||
|
+ return 0;
|
||
|
+}
|
||
|
|
||
|
static int wcnss_notif_cb(struct notifier_block *this, unsigned long code,
|
||
|
void *ss_handle)
|
||
|
@@ -3444,6 +3446,7 @@ static const struct file_operations wcnss_node_fops = {
|
||
|
.open = wcnss_node_open,
|
||
|
.read = wcnss_wlan_read,
|
||
|
.write = wcnss_wlan_write,
|
||
|
+ .release = wcnss_node_release,
|
||
|
};
|
||
|
|
||
|
static struct miscdevice wcnss_misc = {
|
||
|
@@ -3471,6 +3474,13 @@ wcnss_wlan_probe(struct platform_device *pdev)
|
||
|
}
|
||
|
penv->pdev = pdev;
|
||
|
|
||
|
+ penv->user_cal_data =
|
||
|
+ devm_kzalloc(&pdev->dev, MAX_CALIBRATED_DATA_SIZE, GFP_KERNEL);
|
||
|
+ if (!penv->user_cal_data) {
|
||
|
+ dev_err(&pdev->dev, "Failed to alloc memory for cal data.\n");
|
||
|
+ return -ENOMEM;
|
||
|
+ }
|
||
|
+
|
||
|
/* register sysfs entries */
|
||
|
ret = wcnss_create_sysfs(&pdev->dev);
|
||
|
if (ret) {
|
||
|
@@ -3491,6 +3501,11 @@ wcnss_wlan_probe(struct platform_device *pdev)
|
||
|
mutex_init(&penv->pm_qos_mutex);
|
||
|
init_waitqueue_head(&penv->read_wait);
|
||
|
|
||
|
+ penv->user_cal_rcvd = 0;
|
||
|
+ penv->user_cal_read = 0;
|
||
|
+ penv->user_cal_exp_size = 0;
|
||
|
+ penv->user_cal_available = false;
|
||
|
+
|
||
|
/* Since we were built into the kernel we'll be called as part
|
||
|
* of kernel initialization. We don't know if userspace
|
||
|
* applications are available to service PIL at this time
|
||
|
--
|
||
|
cgit v1.1
|
||
|
|