Add a mutex fixing a potential NULL pointer dereference in the pi433
driver.

If pi433_release and pi433_ioctl are concurrently called,
pi433_release might set filp->private_data to NULL while pi433_ioctl
is still accessing it, leading to NULL pointer dereference. This issue
might also affect pi433_write and pi433_read.

The newly introduced mutex makes sure that instance data
will not be modified simultaneously by pi433_release, pi433_write,
pi433_read or pi433_ioctl.

The mutex is stored in a newly introduced struct pi433_data, which
wraps struct pi433_instance and its mutex.

Make filp->private_data point to a struct pi433_data, allowing to
acquire the lock before accessing the struct pi433_instance.

Signed-off-by: Hugo Lefeuvre <h...@owl.eu.com>
---
Changes in v2:
        - Use mutex instead of rw semaphore. 
        - Introduce struct pi433_data in order to allow functions to lock
          before dereferencing instance pointer.
        - Make filp->private_data point to a struct pi433_data.
        - Add missing braces.
---
 drivers/staging/pi433/pi433_if.c | 77 +++++++++++++++++++++++++-------
 1 file changed, 62 insertions(+), 15 deletions(-)

diff --git a/drivers/staging/pi433/pi433_if.c b/drivers/staging/pi433/pi433_if.c
index d1e0ddbc79ce..b56dac27e7f1 100644
--- a/drivers/staging/pi433/pi433_if.c
+++ b/drivers/staging/pi433/pi433_if.c
@@ -118,6 +118,11 @@ struct pi433_instance {
        struct pi433_tx_cfg     tx_cfg;
 };
 
+struct pi433_data {
+       struct pi433_instance   *instance;
+       struct mutex            instance_lock; /* guards instance removal */
+};
+
 /*-------------------------------------------------------------------------*/
 
 /* GPIO interrupt handlers */
@@ -769,6 +774,7 @@ pi433_tx_thread(void *data)
 static ssize_t
 pi433_read(struct file *filp, char __user *buf, size_t size, loff_t *f_pos)
 {
+       struct pi433_data       *data;
        struct pi433_instance   *instance;
        struct pi433_device     *device;
        int                     bytes_received;
@@ -778,13 +784,16 @@ pi433_read(struct file *filp, char __user *buf, size_t 
size, loff_t *f_pos)
        if (size > MAX_MSG_SIZE)
                return -EMSGSIZE;
 
-       instance = filp->private_data;
+       data = filp->private_data;
+       mutex_lock(&data->instance_lock);
+       instance = data->instance;
        device = instance->device;
 
        /* just one read request at a time */
        mutex_lock(&device->rx_lock);
        if (device->rx_active) {
                mutex_unlock(&device->rx_lock);
+               mutex_unlock(&data->instance_lock);
                return -EAGAIN;
        }
 
@@ -804,10 +813,13 @@ pi433_read(struct file *filp, char __user *buf, size_t 
size, loff_t *f_pos)
        /* if read was successful copy to user space*/
        if (bytes_received > 0) {
                retval = copy_to_user(buf, device->rx_buffer, bytes_received);
-               if (retval)
+               if (retval) {
+                       mutex_unlock(&data->instance_lock);
                        return -EFAULT;
+               }
        }
 
+       mutex_unlock(&data->instance_lock);
        return bytes_received;
 }
 
@@ -815,17 +827,22 @@ static ssize_t
 pi433_write(struct file *filp, const char __user *buf,
            size_t count, loff_t *f_pos)
 {
+       struct pi433_data       *data;
        struct pi433_instance   *instance;
        struct pi433_device     *device;
        int                     retval;
        unsigned int            copied;
 
-       instance = filp->private_data;
+       data = filp->private_data;
+       mutex_lock(&data->instance_lock);
+       instance = data->instance;
        device = instance->device;
 
        /* check, whether internal buffer (tx thread) is big enough for 
requested size */
-       if (count > MAX_MSG_SIZE)
+       if (count > MAX_MSG_SIZE) {
+               mutex_unlock(&data->instance_lock);
                return -EMSGSIZE;
+       }
 
        /* write the following sequence into fifo:
         * - tx_cfg
@@ -851,6 +868,7 @@ pi433_write(struct file *filp, const char __user *buf,
        /* start transfer */
        wake_up_interruptible(&device->tx_wait_queue);
        dev_dbg(device->dev, "write: generated new msg with %d bytes.", copied);
+       mutex_unlock(&data->instance_lock);
 
        return copied;
 
@@ -858,6 +876,7 @@ pi433_write(struct file *filp, const char __user *buf,
        dev_dbg(device->dev, "write to fifo failed: 0x%x", retval);
        kfifo_reset(&device->tx_fifo); // TODO: maybe find a solution, not to 
discard already stored, valid entries
        mutex_unlock(&device->tx_fifo_lock);
+       mutex_unlock(&data->instance_lock);
        return -EAGAIN;
 }
 
@@ -865,6 +884,7 @@ static long
 pi433_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
        int                     retval = 0;
+       struct pi433_data       *data;
        struct pi433_instance   *instance;
        struct pi433_device     *device;
        void __user *argp = (void __user *)arg;
@@ -873,30 +893,37 @@ pi433_ioctl(struct file *filp, unsigned int cmd, unsigned 
long arg)
        if (_IOC_TYPE(cmd) != PI433_IOC_MAGIC)
                return -ENOTTY;
 
-       /* TODO? guard against device removal before, or while,
-        * we issue this ioctl. --> device_get()
-        */
-       instance = filp->private_data;
+       data = filp->private_data;
+       mutex_lock(&data->instance_lock);
+       instance = data->instance;
        device = instance->device;
 
-       if (!device)
+       if (!device) {
+               mutex_unlock(&data->instance_lock);
                return -ESHUTDOWN;
+       }
 
        switch (cmd) {
        case PI433_IOC_RD_TX_CFG:
                if (copy_to_user(argp, &instance->tx_cfg,
-                                sizeof(struct pi433_tx_cfg)))
+                                sizeof(struct pi433_tx_cfg))) {
+                       mutex_unlock(&data->instance_lock);
                        return -EFAULT;
+               }
                break;
        case PI433_IOC_WR_TX_CFG:
                if (copy_from_user(&instance->tx_cfg, argp,
-                                  sizeof(struct pi433_tx_cfg)))
+                                  sizeof(struct pi433_tx_cfg))) {
+                       mutex_unlock(&data->instance_lock);
                        return -EFAULT;
+               }
                break;
        case PI433_IOC_RD_RX_CFG:
                if (copy_to_user(argp, &device->rx_cfg,
-                                sizeof(struct pi433_rx_cfg)))
+                                sizeof(struct pi433_rx_cfg))) {
+                       mutex_unlock(&data->instance_lock);
                        return -EFAULT;
+               }
                break;
        case PI433_IOC_WR_RX_CFG:
                mutex_lock(&device->rx_lock);
@@ -904,21 +931,26 @@ pi433_ioctl(struct file *filp, unsigned int cmd, unsigned 
long arg)
                /* during pendig read request, change of config not allowed */
                if (device->rx_active) {
                        mutex_unlock(&device->rx_lock);
+                       mutex_unlock(&data->instance_lock);
                        return -EAGAIN;
                }
 
                if (copy_from_user(&device->rx_cfg, argp,
                                   sizeof(struct pi433_rx_cfg))) {
                        mutex_unlock(&device->rx_lock);
+                       mutex_unlock(&data->instance_lock);
                        return -EFAULT;
                }
 
                mutex_unlock(&device->rx_lock);
+               mutex_unlock(&data->instance_lock);
                break;
        default:
+               mutex_unlock(&data->instance_lock);
                retval = -EINVAL;
        }
 
+       mutex_unlock(&data->instance_lock);
        return retval;
 }
 
@@ -936,6 +968,7 @@ pi433_compat_ioctl(struct file *filp, unsigned int cmd, 
unsigned long arg)
 
 static int pi433_open(struct inode *inode, struct file *filp)
 {
+       struct pi433_data       *data;
        struct pi433_device     *device;
        struct pi433_instance   *instance;
 
@@ -954,20 +987,30 @@ static int pi433_open(struct inode *inode, struct file 
*filp)
        }
 
        device->users++;
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data) {
+               kfree(device->rx_buffer);
+               device->rx_buffer = NULL;
+               return -ENOMEM;
+       }
+       mutex_init(&data->instance_lock);
+
        instance = kzalloc(sizeof(*instance), GFP_KERNEL);
        if (!instance) {
+               kfree(data);
                kfree(device->rx_buffer);
                device->rx_buffer = NULL;
                return -ENOMEM;
        }
 
        /* setup instance data*/
+       data->instance = instance;
        instance->device = device;
        instance->tx_cfg.bit_rate = 4711;
        // TODO: fill instance->tx_cfg;
 
-       /* instance data as context */
-       filp->private_data = instance;
+       /* instance data wrapper as context */
+       filp->private_data = data;
        nonseekable_open(inode, filp);
 
        return 0;
@@ -975,12 +1018,16 @@ static int pi433_open(struct inode *inode, struct file 
*filp)
 
 static int pi433_release(struct inode *inode, struct file *filp)
 {
+       struct pi433_data       *data;
        struct pi433_instance   *instance;
        struct pi433_device     *device;
 
-       instance = filp->private_data;
+       data = filp->private_data;
+       mutex_lock(&data->instance_lock);
+       instance = data->instance;
        device = instance->device;
        kfree(instance);
+       kfree(data);
        filp->private_data = NULL;
 
        /* last close? */
-- 
2.17.1

Attachment: signature.asc
Description: PGP signature

_______________________________________________
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

Reply via email to