From 02d6710e2e018e4704e95f3c8fab9e6cf8249fc3 Mon Sep 17 00:00:00 2001 From: Sarada Prasanna Garnayak 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 --- drivers/net/wireless/wcnss/wcnss_wlan.c | 87 +++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 36 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c index 9db2871e8150..450b7ad8bf7f 100644 --- a/drivers/net/wireless/wcnss/wcnss_wlan.c +++ b/drivers/net/wireless/wcnss/wcnss_wlan.c @@ -398,7 +398,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]; @@ -3265,14 +3264,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; } @@ -3281,7 +3272,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 @@ -3318,55 +3309,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) @@ -3425,6 +3427,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 = { @@ -3452,6 +3455,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) { @@ -3472,6 +3482,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.2.3