[PATCH] media: uvcvideo: read bulk URBs after maxPayloadSize

From: Julian Meyer
Date: Wed Apr 01 2020 - 22:07:27 EST


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