ChangeSet 1.1315.8.24, 2003/09/17 17:59:40-07:00, stern@rowland.harvard.edu

[PATCH] USB: Changes to core/config.c (5 of 9)

This patch centralizes the error checking for invalid descriptor lengths
and unexpected descriptor types.  Instead of doing it in three different
places -- while parsing configuration, interface, and endpoint descriptors
-- the new code does it all at once.  Not surprisingly, this yields a net
savings in code size.


 drivers/usb/core/config.c |   76 ++++++++++++++++++++++------------------------
 1 files changed, 37 insertions(+), 39 deletions(-)


diff -Nru a/drivers/usb/core/config.c b/drivers/usb/core/config.c
--- a/drivers/usb/core/config.c	Fri Sep 19 17:09:44 2003
+++ b/drivers/usb/core/config.c	Fri Sep 19 17:09:44 2003
@@ -37,23 +37,15 @@
 	buffer += header->bLength;
 	size -= header->bLength;
 
-	/* Skip over the rest of the Class Specific or Vendor Specific */
-	/*  descriptors */
+	/* Skip over any Class Specific or Vendor Specific descriptors */
 	begin = buffer;
 	numskipped = 0;
 	while (size >= sizeof(struct usb_descriptor_header)) {
 		header = (struct usb_descriptor_header *)buffer;
 
-		if (header->bLength < 2) {
-			err("invalid descriptor length of %d", header->bLength);
-			return -EINVAL;
-		}
-
 		/* If we find another "proper" descriptor then we're done  */
 		if ((header->bDescriptorType == USB_DT_ENDPOINT) ||
-		    (header->bDescriptorType == USB_DT_INTERFACE) ||
-		    (header->bDescriptorType == USB_DT_CONFIG) ||
-		    (header->bDescriptorType == USB_DT_DEVICE))
+		    (header->bDescriptorType == USB_DT_INTERFACE))
 			break;
 
 		dbg("skipping descriptor 0x%X", header->bDescriptorType);
@@ -155,27 +147,18 @@
 
 		memcpy(&ifp->desc, buffer, USB_DT_INTERFACE_SIZE);
 
-		/* Skip over the interface */
 		buffer += ifp->desc.bLength;
 		size -= ifp->desc.bLength;
 
+		/* Skip over any Class Specific or Vendor Specific descriptors */
 		begin = buffer;
 		numskipped = 0;
-
-		/* Skip over any interface, class or vendor descriptors */
 		while (size >= sizeof(struct usb_descriptor_header)) {
 			header = (struct usb_descriptor_header *)buffer;
 
-			if (header->bLength < 2) {
-				err("invalid descriptor length of %d", header->bLength);
-				return -EINVAL;
-			}
-
 			/* If we find another "proper" descriptor then we're done  */
 			if ((header->bDescriptorType == USB_DT_INTERFACE) ||
-			    (header->bDescriptorType == USB_DT_ENDPOINT) ||
-			    (header->bDescriptorType == USB_DT_CONFIG) ||
-			    (header->bDescriptorType == USB_DT_DEVICE))
+			    (header->bDescriptorType == USB_DT_ENDPOINT))
 				break;
 
 			dbg("skipping descriptor 0x%X", header->bDescriptorType);
@@ -184,7 +167,6 @@
 			buffer += header->bLength;
 			size -= header->bLength;
 		}
-
 		if (numskipped) {
 			dbg("skipped %d class/vendor specific interface descriptors", numskipped);
 
@@ -201,13 +183,6 @@
 			ifp->extralen = len;
 		}
 
-		/* Did we hit an unexpected descriptor? */
-		header = (struct usb_descriptor_header *)buffer;
-		if ((size >= sizeof(struct usb_descriptor_header)) &&
-		    ((header->bDescriptorType == USB_DT_CONFIG) ||
-		     (header->bDescriptorType == USB_DT_DEVICE)))
-			break;
-
 		if (ifp->desc.bNumEndpoints > USB_MAXENDPOINTS) {
 			warn("too many endpoints");
 			return -EINVAL;
@@ -249,11 +224,13 @@
 int usb_parse_configuration(struct usb_host_config *config, char *buffer)
 {
 	int nintf;
-	int i, size;
+	int i, j, size;
 	struct usb_interface *interface;
+	char *buffer2;
+	int size2;
+	struct usb_descriptor_header *header;
 	int numskipped, len;
 	char *begin;
-	struct usb_descriptor_header *header;
 	int retval;
 
 	memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
@@ -283,6 +260,34 @@
 		get_device(&interface->dev);
 	}
 
+	/* Go through the descriptors, checking their length */
+	buffer2 = buffer;
+	size2 = size;
+	j = 0;
+	while (size2 >= sizeof(struct usb_descriptor_header)) {
+		header = (struct usb_descriptor_header *) buffer2;
+		if ((header->bLength > size2) || (header->bLength < 2)) {
+			err("invalid descriptor of length %d", header->bLength);
+			return -EINVAL;
+		}
+
+		if (header->bDescriptorType == USB_DT_INTERFACE) {
+			if (header->bLength < USB_DT_INTERFACE_SIZE) {
+				warn("invalid interface descriptor");
+				return -EINVAL;
+			}
+
+		} else if ((header->bDescriptorType == USB_DT_DEVICE ||
+		    header->bDescriptorType == USB_DT_CONFIG) && j) {
+			warn("unexpected descriptor type 0x%X", header->bDescriptorType);
+			return -EINVAL;
+		}
+
+		j = 1;
+		buffer2 += header->bLength;
+		size2 -= header->bLength;
+	}
+
 	buffer += config->desc.bLength;
 	size -= config->desc.bLength;
 
@@ -292,16 +297,9 @@
 	while (size >= sizeof(struct usb_descriptor_header)) {
 		header = (struct usb_descriptor_header *)buffer;
 
-		if ((header->bLength > size) || (header->bLength < 2)) {
-			err("invalid descriptor length of %d", header->bLength);
-			return -EINVAL;
-		}
-
 		/* If we find another "proper" descriptor then we're done  */
 		if ((header->bDescriptorType == USB_DT_ENDPOINT) ||
-		    (header->bDescriptorType == USB_DT_INTERFACE) ||
-		    (header->bDescriptorType == USB_DT_CONFIG) ||
-		    (header->bDescriptorType == USB_DT_DEVICE))
+		    (header->bDescriptorType == USB_DT_INTERFACE))
 			break;
 
 		dbg("skipping descriptor 0x%X", header->bDescriptorType);
