diff options
author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2012-03-27 04:51:00 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-04-09 09:15:28 -0400 |
commit | ed0ee0ce0a3224dab5caa088a5f8b6df25924276 (patch) | |
tree | 710fb6f35ee2bf9e0ccd1aa8d3318ebb2b677f7f /drivers | |
parent | d3a92d624806a7964ca3122f917ff2ba69e4cdd8 (diff) |
[media] uvcvideo: Fix race-related crash in uvc_video_clock_update()
The driver frees the clock samples buffer before stopping the video
buffers queue. If a DQBUF call arrives in-between,
uvc_video_clock_update() will be called with a NULL clock samples
buffer, leading to a crash. This occurs very frequently when using the
webcam with the flash browser plugin.
Move clock initialization/cleanup to uvc_video_enable() in order to free
the clock samples buffer after the queue is stopped. Make sure the clock
is reset at resume time to avoid miscalculating timestamps.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Cc: stable@vger.kernel.org
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/video/uvc/uvc_video.c | 50 |
1 files changed, 32 insertions, 18 deletions
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index 4a44f9a1bae0..b76b0ac0958f 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c | |||
@@ -468,22 +468,30 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, | |||
468 | spin_unlock_irqrestore(&stream->clock.lock, flags); | 468 | spin_unlock_irqrestore(&stream->clock.lock, flags); |
469 | } | 469 | } |
470 | 470 | ||
471 | static int uvc_video_clock_init(struct uvc_streaming *stream) | 471 | static void uvc_video_clock_reset(struct uvc_streaming *stream) |
472 | { | 472 | { |
473 | struct uvc_clock *clock = &stream->clock; | 473 | struct uvc_clock *clock = &stream->clock; |
474 | 474 | ||
475 | spin_lock_init(&clock->lock); | ||
476 | clock->head = 0; | 475 | clock->head = 0; |
477 | clock->count = 0; | 476 | clock->count = 0; |
478 | clock->size = 32; | ||
479 | clock->last_sof = -1; | 477 | clock->last_sof = -1; |
480 | clock->sof_offset = -1; | 478 | clock->sof_offset = -1; |
479 | } | ||
480 | |||
481 | static int uvc_video_clock_init(struct uvc_streaming *stream) | ||
482 | { | ||
483 | struct uvc_clock *clock = &stream->clock; | ||
484 | |||
485 | spin_lock_init(&clock->lock); | ||
486 | clock->size = 32; | ||
481 | 487 | ||
482 | clock->samples = kmalloc(clock->size * sizeof(*clock->samples), | 488 | clock->samples = kmalloc(clock->size * sizeof(*clock->samples), |
483 | GFP_KERNEL); | 489 | GFP_KERNEL); |
484 | if (clock->samples == NULL) | 490 | if (clock->samples == NULL) |
485 | return -ENOMEM; | 491 | return -ENOMEM; |
486 | 492 | ||
493 | uvc_video_clock_reset(stream); | ||
494 | |||
487 | return 0; | 495 | return 0; |
488 | } | 496 | } |
489 | 497 | ||
@@ -1424,8 +1432,6 @@ static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers) | |||
1424 | 1432 | ||
1425 | if (free_buffers) | 1433 | if (free_buffers) |
1426 | uvc_free_urb_buffers(stream); | 1434 | uvc_free_urb_buffers(stream); |
1427 | |||
1428 | uvc_video_clock_cleanup(stream); | ||
1429 | } | 1435 | } |
1430 | 1436 | ||
1431 | /* | 1437 | /* |
@@ -1555,10 +1561,6 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) | |||
1555 | 1561 | ||
1556 | uvc_video_stats_start(stream); | 1562 | uvc_video_stats_start(stream); |
1557 | 1563 | ||
1558 | ret = uvc_video_clock_init(stream); | ||
1559 | if (ret < 0) | ||
1560 | return ret; | ||
1561 | |||
1562 | if (intf->num_altsetting > 1) { | 1564 | if (intf->num_altsetting > 1) { |
1563 | struct usb_host_endpoint *best_ep = NULL; | 1565 | struct usb_host_endpoint *best_ep = NULL; |
1564 | unsigned int best_psize = 3 * 1024; | 1566 | unsigned int best_psize = 3 * 1024; |
@@ -1683,6 +1685,8 @@ int uvc_video_resume(struct uvc_streaming *stream, int reset) | |||
1683 | 1685 | ||
1684 | stream->frozen = 0; | 1686 | stream->frozen = 0; |
1685 | 1687 | ||
1688 | uvc_video_clock_reset(stream); | ||
1689 | |||
1686 | ret = uvc_commit_video(stream, &stream->ctrl); | 1690 | ret = uvc_commit_video(stream, &stream->ctrl); |
1687 | if (ret < 0) { | 1691 | if (ret < 0) { |
1688 | uvc_queue_enable(&stream->queue, 0); | 1692 | uvc_queue_enable(&stream->queue, 0); |
@@ -1819,25 +1823,35 @@ int uvc_video_enable(struct uvc_streaming *stream, int enable) | |||
1819 | uvc_uninit_video(stream, 1); | 1823 | uvc_uninit_video(stream, 1); |
1820 | usb_set_interface(stream->dev->udev, stream->intfnum, 0); | 1824 | usb_set_interface(stream->dev->udev, stream->intfnum, 0); |
1821 | uvc_queue_enable(&stream->queue, 0); | 1825 | uvc_queue_enable(&stream->queue, 0); |
1826 | uvc_video_clock_cleanup(stream); | ||
1822 | return 0; | 1827 | return 0; |
1823 | } | 1828 | } |
1824 | 1829 | ||
1825 | ret = uvc_queue_enable(&stream->queue, 1); | 1830 | ret = uvc_video_clock_init(stream); |
1826 | if (ret < 0) | 1831 | if (ret < 0) |
1827 | return ret; | 1832 | return ret; |
1828 | 1833 | ||
1834 | ret = uvc_queue_enable(&stream->queue, 1); | ||
1835 | if (ret < 0) | ||
1836 | goto error_queue; | ||
1837 | |||
1829 | /* Commit the streaming parameters. */ | 1838 | /* Commit the streaming parameters. */ |
1830 | ret = uvc_commit_video(stream, &stream->ctrl); | 1839 | ret = uvc_commit_video(stream, &stream->ctrl); |
1831 | if (ret < 0) { | 1840 | if (ret < 0) |
1832 | uvc_queue_enable(&stream->queue, 0); | 1841 | goto error_commit; |
1833 | return ret; | ||
1834 | } | ||
1835 | 1842 | ||
1836 | ret = uvc_init_video(stream, GFP_KERNEL); | 1843 | ret = uvc_init_video(stream, GFP_KERNEL); |
1837 | if (ret < 0) { | 1844 | if (ret < 0) |
1838 | usb_set_interface(stream->dev->udev, stream->intfnum, 0); | 1845 | goto error_video; |
1839 | uvc_queue_enable(&stream->queue, 0); | 1846 | |
1840 | } | 1847 | return 0; |
1848 | |||
1849 | error_video: | ||
1850 | usb_set_interface(stream->dev->udev, stream->intfnum, 0); | ||
1851 | error_commit: | ||
1852 | uvc_queue_enable(&stream->queue, 0); | ||
1853 | error_queue: | ||
1854 | uvc_video_clock_cleanup(stream); | ||
1841 | 1855 | ||
1842 | return ret; | 1856 | return ret; |
1843 | } | 1857 | } |