3.2.92-rc1 review patch.  If anyone has any objections, please let me know.

------------------

From: Alexey Khoroshilov <khoroshi...@ispras.ru>

commit 256d013a9bcc9a39b2e4b34ab19219bd054cf270 upstream.

There are numerous issues in error handling code of cx231xx initialization.
Double free (when cx231xx_init_dev() calls kfree(dev) via 
cx231xx_release_resources()
and then cx231xx_usb_probe() does the same) and memory leaks
(e.g. usb_get_dev() before (ifnum != 1) check in cx231xx_usb_probe())
are just a few of them.
The patch fixes the issues in cx231xx_usb_probe() and cx231xx_init_dev()
by moving usb_get_dev(interface_to_usbdev(interface)) below in code and
implementing proper error handling.
Found by Linux Driver Verification project (linuxtesting.org).

Signed-off-by: Alexey Khoroshilov <khoroshi...@ispras.ru>
Signed-off-by: Hans Verkuil <hans.verk...@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <m.che...@samsung.com>
[bwh: Backported to 3.2:
 - Keep using &= rather than clear_bit()
 - Adjust filename, context
Signed-off-by: Ben Hutchings <b...@decadent.org.uk>
---
 drivers/media/video/cx231xx/cx231xx-cards.c | 110 
++++++++++++++++--------------
 1 file changed, 57 insertions(+), 53 deletions(-)

--- a/drivers/media/video/cx231xx/cx231xx-cards.c
+++ b/drivers/media/video/cx231xx/cx231xx-cards.c
@@ -863,7 +863,6 @@ static int cx231xx_init_dev(struct cx231
 {
        struct cx231xx *dev = *devhandle;
        int retval = -ENOMEM;
-       int errCode;
        unsigned int maxh, maxw;
 
        dev->udev = udev;
@@ -899,8 +898,8 @@ static int cx231xx_init_dev(struct cx231
        /* Cx231xx pre card setup */
        cx231xx_pre_card_setup(dev);
 
-       errCode = cx231xx_config(dev);
-       if (errCode) {
+       retval = cx231xx_config(dev);
+       if (retval) {
                cx231xx_errdev("error configuring device\n");
                return -ENOMEM;
        }
@@ -909,12 +908,11 @@ static int cx231xx_init_dev(struct cx231
        dev->norm = dev->board.norm;
 
        /* register i2c bus */
-       errCode = cx231xx_dev_init(dev);
-       if (errCode < 0) {
-               cx231xx_dev_uninit(dev);
+       retval = cx231xx_dev_init(dev);
+       if (retval) {
                cx231xx_errdev("%s: cx231xx_i2c_register - errCode [%d]!\n",
-                              __func__, errCode);
-               return errCode;
+                              __func__, retval);
+               goto err_dev_init;
        }
 
        /* Do board specific init */
@@ -932,11 +930,11 @@ static int cx231xx_init_dev(struct cx231
        dev->interlaced = 0;
        dev->video_input = 0;
 
-       errCode = cx231xx_config(dev);
-       if (errCode < 0) {
+       retval = cx231xx_config(dev);
+       if (retval) {
                cx231xx_errdev("%s: cx231xx_config - errCode [%d]!\n",
-                              __func__, errCode);
-               return errCode;
+                              __func__, retval);
+               goto err_dev_init;
        }
 
        /* init video dma queues */
@@ -960,9 +958,9 @@ static int cx231xx_init_dev(struct cx231
        }
 
        retval = cx231xx_register_analog_devices(dev);
-       if (retval < 0) {
-               cx231xx_release_resources(dev);
-               return retval;
+       if (retval) {
+               cx231xx_release_analog_resources(dev);
+               goto err_analog;
        }
 
        cx231xx_ir_init(dev);
@@ -970,6 +968,11 @@ static int cx231xx_init_dev(struct cx231
        cx231xx_init_extension(dev);
 
        return 0;
+err_analog:
+       cx231xx_remove_from_devlist(dev);
+err_dev_init:
+       cx231xx_dev_uninit(dev);
+       return retval;
 }
 
 #if defined(CONFIG_MODULES) && defined(MODULE)
@@ -1019,7 +1022,6 @@ static int cx231xx_usb_probe(struct usb_
        struct usb_interface *lif = NULL;
        struct usb_interface_assoc_descriptor *assoc_desc;
 
-       udev = usb_get_dev(interface_to_usbdev(interface));
        ifnum = interface->altsetting[0].desc.bInterfaceNumber;
 
        /*
@@ -1048,6 +1050,8 @@ static int cx231xx_usb_probe(struct usb_
                return -ENOMEM;
        }
 
+       udev = usb_get_dev(interface_to_usbdev(interface));
+
        snprintf(dev->name, 29, "cx231xx #%d", nr);
        dev->devno = nr;
        dev->model = id->driver_info;
@@ -1126,10 +1130,8 @@ static int cx231xx_usb_probe(struct usb_
        if (assoc_desc->bFirstInterface != ifnum) {
                cx231xx_err(DRIVER_NAME ": Not found "
                            "matching IAD interface\n");
-               cx231xx_devused &= ~(1 << nr);
-               kfree(dev);
-               dev = NULL;
-               return -ENODEV;
+               retval = -ENODEV;
+               goto err_if;
        }
 
        cx231xx_info("registering interface %d\n", ifnum);
@@ -1145,22 +1147,13 @@ static int cx231xx_usb_probe(struct usb_
        retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
        if (retval) {
                cx231xx_errdev("v4l2_device_register failed\n");
-               cx231xx_devused &= ~(1 << nr);
-               kfree(dev);
-               dev = NULL;
-               return -EIO;
+               retval = -EIO;
+               goto err_v4l2;
        }
        /* allocate device struct */
        retval = cx231xx_init_dev(&dev, udev, nr);
-       if (retval) {
-               cx231xx_devused &= ~(1 << dev->devno);
-               v4l2_device_unregister(&dev->v4l2_dev);
-               kfree(dev);
-               dev = NULL;
-               usb_set_intfdata(lif, NULL);
-
-               return retval;
-       }
+       if (retval)
+               goto err_init;
 
        /* compute alternate max packet sizes for video */
        uif = udev->actconfig->interface[dev->current_pcb_config.
@@ -1178,11 +1171,8 @@ static int cx231xx_usb_probe(struct usb_
 
        if (dev->video_mode.alt_max_pkt_size == NULL) {
                cx231xx_errdev("out of memory!\n");
-               cx231xx_devused &= ~(1 << nr);
-               v4l2_device_unregister(&dev->v4l2_dev);
-               kfree(dev);
-               dev = NULL;
-               return -ENOMEM;
+               retval = -ENOMEM;
+               goto err_video_alt;
        }
 
        for (i = 0; i < dev->video_mode.num_alt; i++) {
@@ -1212,11 +1202,8 @@ static int cx231xx_usb_probe(struct usb_
 
        if (dev->vbi_mode.alt_max_pkt_size == NULL) {
                cx231xx_errdev("out of memory!\n");
-               cx231xx_devused &= ~(1 << nr);
-               v4l2_device_unregister(&dev->v4l2_dev);
-               kfree(dev);
-               dev = NULL;
-               return -ENOMEM;
+               retval = -ENOMEM;
+               goto err_vbi_alt;
        }
 
        for (i = 0; i < dev->vbi_mode.num_alt; i++) {
@@ -1247,11 +1234,8 @@ static int cx231xx_usb_probe(struct usb_
 
        if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) {
                cx231xx_errdev("out of memory!\n");
-               cx231xx_devused &= ~(1 << nr);
-               v4l2_device_unregister(&dev->v4l2_dev);
-               kfree(dev);
-               dev = NULL;
-               return -ENOMEM;
+               retval = -ENOMEM;
+               goto err_sliced_cc_alt;
        }
 
        for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) {
@@ -1283,11 +1267,8 @@ static int cx231xx_usb_probe(struct usb_
 
                if (dev->ts1_mode.alt_max_pkt_size == NULL) {
                        cx231xx_errdev("out of memory!\n");
-                       cx231xx_devused &= ~(1 << nr);
-                       v4l2_device_unregister(&dev->v4l2_dev);
-                       kfree(dev);
-                       dev = NULL;
-                       return -ENOMEM;
+                       retval = -ENOMEM;
+                       goto err_ts1_alt;
                }
 
                for (i = 0; i < dev->ts1_mode.num_alt; i++) {
@@ -1314,6 +1295,29 @@ static int cx231xx_usb_probe(struct usb_
        request_modules(dev);
 
        return 0;
+err_ts1_alt:
+       kfree(dev->sliced_cc_mode.alt_max_pkt_size);
+err_sliced_cc_alt:
+       kfree(dev->vbi_mode.alt_max_pkt_size);
+err_vbi_alt:
+       kfree(dev->video_mode.alt_max_pkt_size);
+err_video_alt:
+       /* cx231xx_uninit_dev: */
+       cx231xx_close_extension(dev);
+       cx231xx_ir_exit(dev);
+       cx231xx_release_analog_resources(dev);
+       cx231xx_417_unregister(dev);
+       cx231xx_remove_from_devlist(dev);
+       cx231xx_dev_uninit(dev);
+err_init:
+       v4l2_device_unregister(&dev->v4l2_dev);
+err_v4l2:
+       usb_set_intfdata(lif, NULL);
+err_if:
+       usb_put_dev(udev);
+       kfree(dev);
+       cx231xx_devused &= ~(1 << nr);
+       return retval;
 }
 
 /*

Reply via email to