Commit 6e9ffe1a authored by Alexey Khoroshilov's avatar Alexey Khoroshilov Committed by Ben Hutchings

cx231xx: fix double free and leaks on failure path in cx231xx_usb_probe()

commit 256d013a 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: default avatarAlexey Khoroshilov <khoroshilov@ispras.ru>
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <m.chehab@samsung.com>
[bwh: Backported to 3.2:
 - Keep using &= rather than clear_bit()
 - Adjust filename, context
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
parent cc63d977
...@@ -863,7 +863,6 @@ static int cx231xx_init_dev(struct cx231xx **devhandle, struct usb_device *udev, ...@@ -863,7 +863,6 @@ static int cx231xx_init_dev(struct cx231xx **devhandle, struct usb_device *udev,
{ {
struct cx231xx *dev = *devhandle; struct cx231xx *dev = *devhandle;
int retval = -ENOMEM; int retval = -ENOMEM;
int errCode;
unsigned int maxh, maxw; unsigned int maxh, maxw;
dev->udev = udev; dev->udev = udev;
...@@ -899,8 +898,8 @@ static int cx231xx_init_dev(struct cx231xx **devhandle, struct usb_device *udev, ...@@ -899,8 +898,8 @@ static int cx231xx_init_dev(struct cx231xx **devhandle, struct usb_device *udev,
/* Cx231xx pre card setup */ /* Cx231xx pre card setup */
cx231xx_pre_card_setup(dev); cx231xx_pre_card_setup(dev);
errCode = cx231xx_config(dev); retval = cx231xx_config(dev);
if (errCode) { if (retval) {
cx231xx_errdev("error configuring device\n"); cx231xx_errdev("error configuring device\n");
return -ENOMEM; return -ENOMEM;
} }
...@@ -909,12 +908,11 @@ static int cx231xx_init_dev(struct cx231xx **devhandle, struct usb_device *udev, ...@@ -909,12 +908,11 @@ static int cx231xx_init_dev(struct cx231xx **devhandle, struct usb_device *udev,
dev->norm = dev->board.norm; dev->norm = dev->board.norm;
/* register i2c bus */ /* register i2c bus */
errCode = cx231xx_dev_init(dev); retval = cx231xx_dev_init(dev);
if (errCode < 0) { if (retval) {
cx231xx_dev_uninit(dev);
cx231xx_errdev("%s: cx231xx_i2c_register - errCode [%d]!\n", cx231xx_errdev("%s: cx231xx_i2c_register - errCode [%d]!\n",
__func__, errCode); __func__, retval);
return errCode; goto err_dev_init;
} }
/* Do board specific init */ /* Do board specific init */
...@@ -932,11 +930,11 @@ static int cx231xx_init_dev(struct cx231xx **devhandle, struct usb_device *udev, ...@@ -932,11 +930,11 @@ static int cx231xx_init_dev(struct cx231xx **devhandle, struct usb_device *udev,
dev->interlaced = 0; dev->interlaced = 0;
dev->video_input = 0; dev->video_input = 0;
errCode = cx231xx_config(dev); retval = cx231xx_config(dev);
if (errCode < 0) { if (retval) {
cx231xx_errdev("%s: cx231xx_config - errCode [%d]!\n", cx231xx_errdev("%s: cx231xx_config - errCode [%d]!\n",
__func__, errCode); __func__, retval);
return errCode; goto err_dev_init;
} }
/* init video dma queues */ /* init video dma queues */
...@@ -960,9 +958,9 @@ static int cx231xx_init_dev(struct cx231xx **devhandle, struct usb_device *udev, ...@@ -960,9 +958,9 @@ static int cx231xx_init_dev(struct cx231xx **devhandle, struct usb_device *udev,
} }
retval = cx231xx_register_analog_devices(dev); retval = cx231xx_register_analog_devices(dev);
if (retval < 0) { if (retval) {
cx231xx_release_resources(dev); cx231xx_release_analog_resources(dev);
return retval; goto err_analog;
} }
cx231xx_ir_init(dev); cx231xx_ir_init(dev);
...@@ -970,6 +968,11 @@ static int cx231xx_init_dev(struct cx231xx **devhandle, struct usb_device *udev, ...@@ -970,6 +968,11 @@ static int cx231xx_init_dev(struct cx231xx **devhandle, struct usb_device *udev,
cx231xx_init_extension(dev); cx231xx_init_extension(dev);
return 0; return 0;
err_analog:
cx231xx_remove_from_devlist(dev);
err_dev_init:
cx231xx_dev_uninit(dev);
return retval;
} }
#if defined(CONFIG_MODULES) && defined(MODULE) #if defined(CONFIG_MODULES) && defined(MODULE)
...@@ -1019,7 +1022,6 @@ static int cx231xx_usb_probe(struct usb_interface *interface, ...@@ -1019,7 +1022,6 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
struct usb_interface *lif = NULL; struct usb_interface *lif = NULL;
struct usb_interface_assoc_descriptor *assoc_desc; struct usb_interface_assoc_descriptor *assoc_desc;
udev = usb_get_dev(interface_to_usbdev(interface));
ifnum = interface->altsetting[0].desc.bInterfaceNumber; ifnum = interface->altsetting[0].desc.bInterfaceNumber;
/* /*
...@@ -1048,6 +1050,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface, ...@@ -1048,6 +1050,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
return -ENOMEM; return -ENOMEM;
} }
udev = usb_get_dev(interface_to_usbdev(interface));
snprintf(dev->name, 29, "cx231xx #%d", nr); snprintf(dev->name, 29, "cx231xx #%d", nr);
dev->devno = nr; dev->devno = nr;
dev->model = id->driver_info; dev->model = id->driver_info;
...@@ -1126,10 +1130,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface, ...@@ -1126,10 +1130,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
if (assoc_desc->bFirstInterface != ifnum) { if (assoc_desc->bFirstInterface != ifnum) {
cx231xx_err(DRIVER_NAME ": Not found " cx231xx_err(DRIVER_NAME ": Not found "
"matching IAD interface\n"); "matching IAD interface\n");
cx231xx_devused &= ~(1 << nr); retval = -ENODEV;
kfree(dev); goto err_if;
dev = NULL;
return -ENODEV;
} }
cx231xx_info("registering interface %d\n", ifnum); cx231xx_info("registering interface %d\n", ifnum);
...@@ -1145,22 +1147,13 @@ static int cx231xx_usb_probe(struct usb_interface *interface, ...@@ -1145,22 +1147,13 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev); retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
if (retval) { if (retval) {
cx231xx_errdev("v4l2_device_register failed\n"); cx231xx_errdev("v4l2_device_register failed\n");
cx231xx_devused &= ~(1 << nr); retval = -EIO;
kfree(dev); goto err_v4l2;
dev = NULL;
return -EIO;
} }
/* allocate device struct */ /* allocate device struct */
retval = cx231xx_init_dev(&dev, udev, nr); retval = cx231xx_init_dev(&dev, udev, nr);
if (retval) { if (retval)
cx231xx_devused &= ~(1 << dev->devno); goto err_init;
v4l2_device_unregister(&dev->v4l2_dev);
kfree(dev);
dev = NULL;
usb_set_intfdata(lif, NULL);
return retval;
}
/* compute alternate max packet sizes for video */ /* compute alternate max packet sizes for video */
uif = udev->actconfig->interface[dev->current_pcb_config. uif = udev->actconfig->interface[dev->current_pcb_config.
...@@ -1178,11 +1171,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface, ...@@ -1178,11 +1171,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
if (dev->video_mode.alt_max_pkt_size == NULL) { if (dev->video_mode.alt_max_pkt_size == NULL) {
cx231xx_errdev("out of memory!\n"); cx231xx_errdev("out of memory!\n");
cx231xx_devused &= ~(1 << nr); retval = -ENOMEM;
v4l2_device_unregister(&dev->v4l2_dev); goto err_video_alt;
kfree(dev);
dev = NULL;
return -ENOMEM;
} }
for (i = 0; i < dev->video_mode.num_alt; i++) { for (i = 0; i < dev->video_mode.num_alt; i++) {
...@@ -1212,11 +1202,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface, ...@@ -1212,11 +1202,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
if (dev->vbi_mode.alt_max_pkt_size == NULL) { if (dev->vbi_mode.alt_max_pkt_size == NULL) {
cx231xx_errdev("out of memory!\n"); cx231xx_errdev("out of memory!\n");
cx231xx_devused &= ~(1 << nr); retval = -ENOMEM;
v4l2_device_unregister(&dev->v4l2_dev); goto err_vbi_alt;
kfree(dev);
dev = NULL;
return -ENOMEM;
} }
for (i = 0; i < dev->vbi_mode.num_alt; i++) { for (i = 0; i < dev->vbi_mode.num_alt; i++) {
...@@ -1247,11 +1234,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface, ...@@ -1247,11 +1234,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) { if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) {
cx231xx_errdev("out of memory!\n"); cx231xx_errdev("out of memory!\n");
cx231xx_devused &= ~(1 << nr); retval = -ENOMEM;
v4l2_device_unregister(&dev->v4l2_dev); goto err_sliced_cc_alt;
kfree(dev);
dev = NULL;
return -ENOMEM;
} }
for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) { for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) {
...@@ -1283,11 +1267,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface, ...@@ -1283,11 +1267,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
if (dev->ts1_mode.alt_max_pkt_size == NULL) { if (dev->ts1_mode.alt_max_pkt_size == NULL) {
cx231xx_errdev("out of memory!\n"); cx231xx_errdev("out of memory!\n");
cx231xx_devused &= ~(1 << nr); retval = -ENOMEM;
v4l2_device_unregister(&dev->v4l2_dev); goto err_ts1_alt;
kfree(dev);
dev = NULL;
return -ENOMEM;
} }
for (i = 0; i < dev->ts1_mode.num_alt; i++) { for (i = 0; i < dev->ts1_mode.num_alt; i++) {
...@@ -1314,6 +1295,29 @@ static int cx231xx_usb_probe(struct usb_interface *interface, ...@@ -1314,6 +1295,29 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
request_modules(dev); request_modules(dev);
return 0; 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;
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment