wil6210: validate wil_pmc_alloc parameters
authorMaya Erez <qca_merez@qca.qualcomm.com>
Mon, 28 Nov 2016 11:49:01 +0000 (13:49 +0200)
committerKalle Valo <kvalo@qca.qualcomm.com>
Thu, 1 Dec 2016 11:20:31 +0000 (13:20 +0200)
num_descriptors and descriptor_size needs to be
checked for:
1) not being negative values
2) no overflow occurs when these are multiplied
together as done in wil_pmc_read.
An overflow of two signed integers is undefined
behavior.

Signed-off-by: Maya Erez <qca_merez@qca.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/wil6210/pmc.c

index 5ca0307a3274dd7ab9cf3d31c950e856aeb343cc..b9faae0278c9b33d85a4666bc94e847f3672dd26 100644 (file)
@@ -54,6 +54,7 @@ void wil_pmc_alloc(struct wil6210_priv *wil,
        struct pmc_ctx *pmc = &wil->pmc;
        struct device *dev = wil_to_dev(wil);
        struct wmi_pmc_cmd pmc_cmd = {0};
+       int last_cmd_err = -ENOMEM;
 
        mutex_lock(&pmc->lock);
 
@@ -62,6 +63,29 @@ void wil_pmc_alloc(struct wil6210_priv *wil,
                wil_err(wil, "%s: ERROR pmc is already allocated\n", __func__);
                goto no_release_err;
        }
+       if ((num_descriptors <= 0) || (descriptor_size <= 0)) {
+               wil_err(wil,
+                       "Invalid params num_descriptors(%d), descriptor_size(%d)\n",
+                       num_descriptors, descriptor_size);
+               last_cmd_err = -EINVAL;
+               goto no_release_err;
+       }
+
+       if (num_descriptors > (1 << WIL_RING_SIZE_ORDER_MAX)) {
+               wil_err(wil,
+                       "num_descriptors(%d) exceeds max ring size %d\n",
+                       num_descriptors, 1 << WIL_RING_SIZE_ORDER_MAX);
+               last_cmd_err = -EINVAL;
+               goto no_release_err;
+       }
+
+       if (num_descriptors > INT_MAX / descriptor_size) {
+               wil_err(wil,
+                       "Overflow in num_descriptors(%d)*descriptor_size(%d)\n",
+                       num_descriptors, descriptor_size);
+               last_cmd_err = -EINVAL;
+               goto no_release_err;
+       }
 
        pmc->num_descriptors = num_descriptors;
        pmc->descriptor_size = descriptor_size;
@@ -189,7 +213,7 @@ release_pmc_skb_list:
        pmc->descriptors = NULL;
 
 no_release_err:
-       pmc->last_cmd_status = -ENOMEM;
+       pmc->last_cmd_status = last_cmd_err;
        mutex_unlock(&pmc->lock);
 }
 
@@ -295,7 +319,7 @@ ssize_t wil_pmc_read(struct file *filp, char __user *buf, size_t count,
        size_t retval = 0;
        unsigned long long idx;
        loff_t offset;
-       size_t pmc_size = pmc->descriptor_size * pmc->num_descriptors;
+       size_t pmc_size;
 
        mutex_lock(&pmc->lock);
 
@@ -306,6 +330,8 @@ ssize_t wil_pmc_read(struct file *filp, char __user *buf, size_t count,
                return -EPERM;
        }
 
+       pmc_size = pmc->descriptor_size * pmc->num_descriptors;
+
        wil_dbg_misc(wil,
                     "%s: size %u, pos %lld\n",
                     __func__, (unsigned)count, *f_pos);
@@ -345,7 +371,18 @@ loff_t wil_pmc_llseek(struct file *filp, loff_t off, int whence)
        loff_t newpos;
        struct wil6210_priv *wil = filp->private_data;
        struct pmc_ctx *pmc = &wil->pmc;
-       size_t pmc_size = pmc->descriptor_size * pmc->num_descriptors;
+       size_t pmc_size;
+
+       mutex_lock(&pmc->lock);
+
+       if (!wil_is_pmc_allocated(pmc)) {
+               wil_err(wil, "error, pmc is not allocated!\n");
+               pmc->last_cmd_status = -EPERM;
+               mutex_unlock(&pmc->lock);
+               return -EPERM;
+       }
+
+       pmc_size = pmc->descriptor_size * pmc->num_descriptors;
 
        switch (whence) {
        case 0: /* SEEK_SET */
@@ -361,15 +398,21 @@ loff_t wil_pmc_llseek(struct file *filp, loff_t off, int whence)
                break;
 
        default: /* can't happen */
-               return -EINVAL;
+               newpos = -EINVAL;
+               goto out;
        }
 
-       if (newpos < 0)
-               return -EINVAL;
+       if (newpos < 0) {
+               newpos = -EINVAL;
+               goto out;
+       }
        if (newpos > pmc_size)
                newpos = pmc_size;
 
        filp->f_pos = newpos;
 
+out:
+       mutex_unlock(&pmc->lock);
+
        return newpos;
 }