DivestOS/Patches/Linux_CVEs/CVE-2017-8280/ANY/0.patch

192 lines
5.7 KiB
Diff
Raw Normal View History

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