aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/uvc/uvc_video.c
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2009-12-16 19:20:45 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2010-01-17 08:31:35 -0500
commitd7c0d43997cf716617d724554d19b3b8dd465833 (patch)
tree00ef45adee02318f35775cc9ecce4ae18372a624 /drivers/media/video/uvc/uvc_video.c
parent2c4d9de8ab1434336248bbc01ee8e64d7e6b8a4f (diff)
V4L/DVB (13831): uvcvideo: Fix oops caused by a race condition in buffer dequeuing
Buffers were marked as done before being removed from the IRQ queue. If a userspace application dequeued and requeued the buffer fast enough during that time window, the buffer could end up being deleted twice, generating an oops in interrupt context. Add a new state, UVC_BUF_STATE_READY, to mark buffers as ready for reuse but not yet removed from the queue, and transition to UVC_BUF_STATE_DONE only when the buffer is removed from the queue. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/uvc/uvc_video.c')
-rw-r--r--drivers/media/video/uvc/uvc_video.c14
1 files changed, 6 insertions, 8 deletions
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
index e8cc0a9ddadd..7dcf534a0cf3 100644
--- a/drivers/media/video/uvc/uvc_video.c
+++ b/drivers/media/video/uvc/uvc_video.c
@@ -441,7 +441,7 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
441 if (fid != stream->last_fid && buf->buf.bytesused != 0) { 441 if (fid != stream->last_fid && buf->buf.bytesused != 0) {
442 uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit " 442 uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit "
443 "toggled).\n"); 443 "toggled).\n");
444 buf->state = UVC_BUF_STATE_DONE; 444 buf->state = UVC_BUF_STATE_READY;
445 return -EAGAIN; 445 return -EAGAIN;
446 } 446 }
447 447
@@ -470,7 +470,7 @@ static void uvc_video_decode_data(struct uvc_streaming *stream,
470 /* Complete the current frame if the buffer size was exceeded. */ 470 /* Complete the current frame if the buffer size was exceeded. */
471 if (len > maxlen) { 471 if (len > maxlen) {
472 uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n"); 472 uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n");
473 buf->state = UVC_BUF_STATE_DONE; 473 buf->state = UVC_BUF_STATE_READY;
474 } 474 }
475} 475}
476 476
@@ -482,7 +482,7 @@ static void uvc_video_decode_end(struct uvc_streaming *stream,
482 uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n"); 482 uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n");
483 if (data[0] == len) 483 if (data[0] == len)
484 uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n"); 484 uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n");
485 buf->state = UVC_BUF_STATE_DONE; 485 buf->state = UVC_BUF_STATE_READY;
486 if (stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) 486 if (stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID)
487 stream->last_fid ^= UVC_STREAM_FID; 487 stream->last_fid ^= UVC_STREAM_FID;
488 } 488 }
@@ -568,8 +568,7 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream,
568 uvc_video_decode_end(stream, buf, mem, 568 uvc_video_decode_end(stream, buf, mem,
569 urb->iso_frame_desc[i].actual_length); 569 urb->iso_frame_desc[i].actual_length);
570 570
571 if (buf->state == UVC_BUF_STATE_DONE || 571 if (buf->state == UVC_BUF_STATE_READY)
572 buf->state == UVC_BUF_STATE_ERROR)
573 buf = uvc_queue_next_buffer(&stream->queue, buf); 572 buf = uvc_queue_next_buffer(&stream->queue, buf);
574 } 573 }
575} 574}
@@ -627,8 +626,7 @@ static void uvc_video_decode_bulk(struct urb *urb, struct uvc_streaming *stream,
627 if (!stream->bulk.skip_payload && buf != NULL) { 626 if (!stream->bulk.skip_payload && buf != NULL) {
628 uvc_video_decode_end(stream, buf, stream->bulk.header, 627 uvc_video_decode_end(stream, buf, stream->bulk.header,
629 stream->bulk.payload_size); 628 stream->bulk.payload_size);
630 if (buf->state == UVC_BUF_STATE_DONE || 629 if (buf->state == UVC_BUF_STATE_READY)
631 buf->state == UVC_BUF_STATE_ERROR)
632 buf = uvc_queue_next_buffer(&stream->queue, 630 buf = uvc_queue_next_buffer(&stream->queue,
633 buf); 631 buf);
634 } 632 }
@@ -669,7 +667,7 @@ static void uvc_video_encode_bulk(struct urb *urb, struct uvc_streaming *stream,
669 stream->bulk.payload_size == stream->bulk.max_payload_size) { 667 stream->bulk.payload_size == stream->bulk.max_payload_size) {
670 if (buf->buf.bytesused == stream->queue.buf_used) { 668 if (buf->buf.bytesused == stream->queue.buf_used) {
671 stream->queue.buf_used = 0; 669 stream->queue.buf_used = 0;
672 buf->state = UVC_BUF_STATE_DONE; 670 buf->state = UVC_BUF_STATE_READY;
673 uvc_queue_next_buffer(&stream->queue, buf); 671 uvc_queue_next_buffer(&stream->queue, buf);
674 stream->last_fid ^= UVC_STREAM_FID; 672 stream->last_fid ^= UVC_STREAM_FID;
675 } 673 }