This fixes a bug that caused certain Realtek cameras to crash. The camera would send additional UVC payloads after the maxPayloadSize was reached. This patch modifies uvc_video_decode_bulk such that it continues reading payloads when it reaches the maxPayloadSize if there is more data left. Signed-off-by: Julian Meyer <julianmeyer2000@xxxxxxxxx> --- drivers/media/usb/uvc/uvc_video.c | 76 +++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 24 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 8fa77a81dd7f..32cc8b21705a 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1374,31 +1374,22 @@ static void uvc_video_decode_isoc(struct uvc_urb *uvc_urb, } } -static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb, - struct uvc_buffer *buf, struct uvc_buffer *meta_buf) +static void uvc_video_decode_bulk_single(struct uvc_streaming *stream, + struct uvc_buffer *buf, struct uvc_buffer *meta_buf, + struct uvc_urb *uvc_urb, u8 **mem, int *len) { - struct urb *urb = uvc_urb->urb; - struct uvc_streaming *stream = uvc_urb->stream; - u8 *mem; - int len, ret; - - /* - * Ignore ZLPs if they're not part of a frame, otherwise process them - * to trigger the end of payload detection. - */ - if (urb->actual_length == 0 && stream->bulk.header_size == 0) - return; + unsigned int bytes_left; + int ret; - mem = urb->transfer_buffer; - len = urb->actual_length; - stream->bulk.payload_size += len; + struct urb *urb = uvc_urb->urb; + unsigned int max_size = stream->bulk.max_payload_size; /* If the URB is the first of its payload, decode and save the * header. */ if (stream->bulk.header_size == 0 && !stream->bulk.skip_payload) { do { - ret = uvc_video_decode_start(stream, buf, mem, len); + ret = uvc_video_decode_start(stream, buf, *mem, *len); if (ret == -EAGAIN) uvc_video_next_buffers(stream, &buf, &meta_buf); } while (ret == -EAGAIN); @@ -1407,13 +1398,14 @@ static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb, if (ret < 0 || buf == NULL) { stream->bulk.skip_payload = 1; } else { - memcpy(stream->bulk.header, mem, ret); + memcpy(stream->bulk.header, *mem, ret); stream->bulk.header_size = ret; - uvc_video_decode_meta(stream, meta_buf, mem, ret); + uvc_video_decode_meta(stream, meta_buf, *mem, ret); - mem += ret; - len -= ret; + *mem += ret; + *len -= ret; + stream->bulk.payload_size += ret; } } @@ -1423,14 +1415,26 @@ static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb, */ /* Prepare video data for processing. */ - if (!stream->bulk.skip_payload && buf != NULL) - uvc_video_decode_data(uvc_urb, buf, mem, len); + if (!stream->bulk.skip_payload && buf != NULL) { + bytes_left = min((unsigned int) *len, + max_size - stream->bulk.payload_size); + + stream->bulk.payload_size += bytes_left; + + uvc_video_decode_data(uvc_urb, buf, *mem, bytes_left); + + *len -= bytes_left; + *mem += bytes_left; + } else { + stream->bulk.payload_size += *len; + *len = 0; + } /* Detect the payload end by a URB smaller than the maximum size (or * a payload size equal to the maximum) and process the header again. */ if (urb->actual_length < urb->transfer_buffer_length || - stream->bulk.payload_size >= stream->bulk.max_payload_size) { + stream->bulk.payload_size >= stream->bulk.max_payload_size) { if (!stream->bulk.skip_payload && buf != NULL) { uvc_video_decode_end(stream, buf, stream->bulk.header, stream->bulk.payload_size); @@ -1444,6 +1448,30 @@ static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb, } } +static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb, + struct uvc_buffer *buf, struct uvc_buffer *meta_buf) +{ + struct urb *urb = uvc_urb->urb; + struct uvc_streaming *stream = uvc_urb->stream; + u8 *mem; + int len; + + /* + * Ignore ZLPs if they're not part of a frame, otherwise process them + * to trigger the end of payload detection. + */ + if (urb->actual_length == 0 && stream->bulk.header_size == 0) + return; + + mem = urb->transfer_buffer; + len = urb->actual_length; + + while (len > 0) { + uvc_video_decode_bulk_single(stream, buf, meta_buf, uvc_urb, + &mem, &len); + } +} + static void uvc_video_encode_bulk(struct uvc_urb *uvc_urb, struct uvc_buffer *buf, struct uvc_buffer *meta_buf) { -- 2.25.0