V4L/DVB (9569): uvcvideo: Sort the frame descriptors during parsing

The UVC specification does not require frame descriptors to be sorted by
bFrameIndex. At least one camera (145f:013e) is known not to sort the
descriptors properly, so they need to be sorted during parsing as the driver
then accesses them by index.

Signed-off-by: Laurent Pinchart <laurent.pinchart@skynet.be>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
index 07cf951..11398ee 100644
--- a/drivers/media/video/uvc/uvc_driver.c
+++ b/drivers/media/video/uvc/uvc_driver.c
@@ -288,8 +288,10 @@
 	struct uvc_format_desc *fmtdesc;
 	struct uvc_frame *frame;
 	const unsigned char *start = buffer;
+	unsigned char *_buffer;
 	unsigned int interval;
 	unsigned int i, n;
+	int _buflen;
 	__u8 ftype;
 
 	format->type = buffer[2];
@@ -410,12 +412,20 @@
 	buflen -= buffer[0];
 	buffer += buffer[0];
 
+	/* Count the number of frame descriptors to test the bFrameIndex
+	 * field when parsing the descriptors. We can't rely on the
+	 * bNumFrameDescriptors field as some cameras don't initialize it
+	 * properly.
+	 */
+	for (_buflen = buflen, _buffer = buffer;
+	     _buflen > 2 && _buffer[2] == ftype;
+	     _buflen -= _buffer[0], _buffer += _buffer[0])
+		format->nframes++;
+
 	/* Parse the frame descriptors. Only uncompressed, MJPEG and frame
 	 * based formats have frame descriptors.
 	 */
 	while (buflen > 2 && buffer[2] == ftype) {
-		frame = &format->frame[format->nframes];
-
 		if (ftype != VS_FRAME_FRAME_BASED)
 			n = buflen > 25 ? buffer[25] : 0;
 		else
@@ -430,6 +440,16 @@
 			return -EINVAL;
 		}
 
+		if (buffer[3] - 1 >= format->nframes) {
+			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
+			       "interface %d frame index %u out of range\n",
+			       dev->udev->devnum, alts->desc.bInterfaceNumber,
+			       buffer[3]);
+			return -EINVAL;
+		}
+
+		frame = &format->frame[buffer[3] - 1];
+
 		frame->bFrameIndex = buffer[3];
 		frame->bmCapabilities = buffer[4];
 		frame->wWidth = le16_to_cpup((__le16 *)&buffer[5]);
@@ -486,7 +506,6 @@
 			10000000/frame->dwDefaultFrameInterval,
 			(100000000/frame->dwDefaultFrameInterval)%10);
 
-		format->nframes++;
 		buflen -= buffer[0];
 		buffer += buffer[0];
 	}