Skip to content

Commit 0fdd7cc

Browse files
ribaldagregkh
authored andcommitted
media: uvcvideo: Fix crash during unbind if gpio unit is in use
commit a9ea1a3 upstream. We used the wrong device for the device managed functions. We used the usb device, when we should be using the interface device. If we unbind the driver from the usb interface, the cleanup functions are never called. In our case, the IRQ is never disabled. If an IRQ is triggered, it will try to access memory sections that are already free, causing an OOPS. We cannot use the function devm_request_threaded_irq here. The devm_* clean functions may be called after the main structure is released by uvc_delete. Luckily this bug has small impact, as it is only affected by devices with gpio units and the user has to unbind the device, a disconnect will not trigger this error. Cc: [email protected] Fixes: 2886477 ("media: uvcvideo: Implement UVC_EXT_GPIO_UNIT") Reviewed-by: Sergey Senozhatsky <[email protected]> Signed-off-by: Ricardo Ribalda <[email protected]> Reviewed-by: Laurent Pinchart <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Laurent Pinchart <[email protected]> Signed-off-by: Mauro Carvalho Chehab <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 607dc72 commit 0fdd7cc

File tree

2 files changed

+24
-12
lines changed

2 files changed

+24
-12
lines changed

drivers/media/usb/uvc/uvc_driver.c

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1515,18 +1515,15 @@ static int uvc_gpio_parse(struct uvc_device *dev)
15151515
struct gpio_desc *gpio_privacy;
15161516
int irq;
15171517

1518-
gpio_privacy = devm_gpiod_get_optional(&dev->udev->dev, "privacy",
1518+
gpio_privacy = devm_gpiod_get_optional(&dev->intf->dev, "privacy",
15191519
GPIOD_IN);
15201520
if (IS_ERR_OR_NULL(gpio_privacy))
15211521
return PTR_ERR_OR_ZERO(gpio_privacy);
15221522

15231523
irq = gpiod_to_irq(gpio_privacy);
1524-
if (irq < 0) {
1525-
if (irq != EPROBE_DEFER)
1526-
dev_err(&dev->udev->dev,
1527-
"No IRQ for privacy GPIO (%d)\n", irq);
1528-
return irq;
1529-
}
1524+
if (irq < 0)
1525+
return dev_err_probe(&dev->intf->dev, irq,
1526+
"No IRQ for privacy GPIO\n");
15301527

15311528
unit = uvc_alloc_entity(UVC_EXT_GPIO_UNIT, UVC_EXT_GPIO_UNIT_ID, 0, 1);
15321529
if (!unit)
@@ -1551,15 +1548,27 @@ static int uvc_gpio_parse(struct uvc_device *dev)
15511548
static int uvc_gpio_init_irq(struct uvc_device *dev)
15521549
{
15531550
struct uvc_entity *unit = dev->gpio_unit;
1551+
int ret;
15541552

15551553
if (!unit || unit->gpio.irq < 0)
15561554
return 0;
15571555

1558-
return devm_request_threaded_irq(&dev->udev->dev, unit->gpio.irq, NULL,
1559-
uvc_gpio_irq,
1560-
IRQF_ONESHOT | IRQF_TRIGGER_FALLING |
1561-
IRQF_TRIGGER_RISING,
1562-
"uvc_privacy_gpio", dev);
1556+
ret = request_threaded_irq(unit->gpio.irq, NULL, uvc_gpio_irq,
1557+
IRQF_ONESHOT | IRQF_TRIGGER_FALLING |
1558+
IRQF_TRIGGER_RISING,
1559+
"uvc_privacy_gpio", dev);
1560+
1561+
unit->gpio.initialized = !ret;
1562+
1563+
return ret;
1564+
}
1565+
1566+
static void uvc_gpio_deinit(struct uvc_device *dev)
1567+
{
1568+
if (!dev->gpio_unit || !dev->gpio_unit->gpio.initialized)
1569+
return;
1570+
1571+
free_irq(dev->gpio_unit->gpio.irq, dev);
15631572
}
15641573

15651574
/* ------------------------------------------------------------------------
@@ -2152,6 +2161,8 @@ static void uvc_unregister_video(struct uvc_device *dev)
21522161
{
21532162
struct uvc_streaming *stream;
21542163

2164+
uvc_gpio_deinit(dev);
2165+
21552166
list_for_each_entry(stream, &dev->streams, list) {
21562167
/* Nothing to do here, continue. */
21572168
if (!video_is_registered(&stream->vdev))

drivers/media/usb/uvc/uvcvideo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ struct uvc_entity {
368368
u8 *bmControls;
369369
struct gpio_desc *gpio_privacy;
370370
int irq;
371+
bool initialized;
371372
} gpio;
372373
};
373374

0 commit comments

Comments
 (0)