ChangeSet 1.1595.7.1, 2003/07/29 11:36:46-07:00, greg@kroah.com

[PATCH] USB: fix stupid kobject coding error with regards to struct usb_interface

Added a release callback, as is required, otherwise we can easily oops
if a user grabs a sysfs file and the device is removed from the system.


 drivers/usb/core/config.c |   94 +++++++++++++++++++++++++---------------------
 include/linux/usb.h       |    5 +-
 2 files changed, 57 insertions(+), 42 deletions(-)


diff -Nru a/drivers/usb/core/config.c b/drivers/usb/core/config.c
--- a/drivers/usb/core/config.c	Fri Aug  1 10:57:02 2003
+++ b/drivers/usb/core/config.c	Fri Aug  1 10:57:02 2003
@@ -98,6 +98,32 @@
 	return parsed;
 }
 
+static void usb_release_intf(struct device *dev)
+{
+	struct usb_interface *intf;
+	int j;
+	int k;
+
+	intf = to_usb_interface(dev);
+
+	if (intf->altsetting) {
+		for (j = 0; j < intf->num_altsetting; j++) {
+			struct usb_host_interface *as = &intf->altsetting[j];
+			if (as->extra)
+				kfree(as->extra);
+
+			if (as->endpoint) {
+				for (k = 0; k < as->desc.bNumEndpoints; k++)
+					if (as->endpoint[k].extra)
+						kfree(as->endpoint[k].extra);
+				kfree(as->endpoint);
+			}
+		}
+		kfree(intf->altsetting);
+	}
+	kfree(intf);
+}
+
 static int usb_parse_interface(struct usb_interface *interface, unsigned char *buffer, int size)
 {
 	int i, len, numskipped, retval, parsed = 0;
@@ -109,7 +135,11 @@
 	interface->num_altsetting = 0;
 	interface->max_altsetting = USB_ALTSETTINGALLOC;
 	device_initialize(&interface->dev);
+	interface->dev.release = usb_release_intf;
 
+	/* put happens in usb_destroy_configuration */
+	get_device(&interface->dev);
+	
 	interface->altsetting = kmalloc(sizeof(*interface->altsetting) * interface->max_altsetting,
 					GFP_KERNEL);
 	
@@ -253,30 +283,33 @@
 
 int usb_parse_configuration(struct usb_host_config *config, char *buffer)
 {
-	int i, retval, size;
+	int i, size;
+	int retval = -EINVAL;
 	struct usb_descriptor_header *header;
 
 	memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
 	le16_to_cpus(&config->desc.wTotalLength);
 	size = config->desc.wTotalLength;
 
+	for (i = 0; i < USB_MAXINTERFACES; ++i)
+		config->interface[i] = NULL;
+
 	if (config->desc.bNumInterfaces > USB_MAXINTERFACES) {
 		warn("too many interfaces");
-		return -1;
+		goto error;
 	}
 
-	config->interface = (struct usb_interface *)
-		kmalloc(config->desc.bNumInterfaces *
-		sizeof(struct usb_interface), GFP_KERNEL);
-	dbg("kmalloc IF %p, numif %i", config->interface, config->desc.bNumInterfaces);
-	if (!config->interface) {
-		err("out of memory");
-		return -1;	
+	for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+		config->interface[i] = kmalloc(sizeof(struct usb_interface), GFP_KERNEL);
+		dbg("kmalloc IF %p, numif %i", config->interface[i], i);
+		if (!config->interface[i]) {
+			err("out of memory");
+			retval = -ENOMEM;
+			goto error;
+		}
+		memset(config->interface[i], 0x00, sizeof(struct usb_interface));
 	}
 
-	memset(config->interface, 0,
-	       config->desc.bNumInterfaces * sizeof(struct usb_interface));
-
 	buffer += config->desc.bLength;
 	size -= config->desc.bLength;
 	
@@ -334,7 +367,7 @@
 			}
 		}
 
-		retval = usb_parse_interface(config->interface + i, buffer, size);
+		retval = usb_parse_interface(config->interface[i], buffer, size);
 		if (retval < 0)
 			return retval;
 
@@ -343,13 +376,17 @@
 	}
 
 	return size;
+error:
+	for (i = 0; i < USB_MAXINTERFACES; ++i)
+		kfree(config->interface[i]);
+	return retval;
 }
 
 // hub-only!! ... and only exported for reset/reinit path.
 // otherwise used internally on disconnect/destroy path
 void usb_destroy_configuration(struct usb_device *dev)
 {
-	int c, i, j, k;
+	int c, i;
 	
 	if (!dev->config)
 		return;
@@ -368,34 +405,9 @@
 			break;
 
 		for (i = 0; i < cf->desc.bNumInterfaces; i++) {
-			struct usb_interface *ifp =
-				&cf->interface[i];
-				
-			if (!ifp->altsetting)
-				break;
-
-			for (j = 0; j < ifp->num_altsetting; j++) {
-				struct usb_host_interface *as =
-					&ifp->altsetting[j];
-					
-				if(as->extra) {
-					kfree(as->extra);
-				}
-
-				if (!as->endpoint)
-					break;
-					
-				for(k = 0; k < as->desc.bNumEndpoints; k++) {
-					if(as->endpoint[k].extra) {
-						kfree(as->endpoint[k].extra);
-					}
-				}	
-				kfree(as->endpoint);
-			}
-
-			kfree(ifp->altsetting);
+			struct usb_interface *ifp = cf->interface[i];
+			put_device(&ifp->dev);
 		}
-		kfree(cf->interface);
 	}
 	kfree(dev->config);
 }
diff -Nru a/include/linux/usb.h b/include/linux/usb.h
--- a/include/linux/usb.h	Fri Aug  1 10:57:02 2003
+++ b/include/linux/usb.h	Fri Aug  1 10:57:02 2003
@@ -140,6 +140,9 @@
 	dev_set_drvdata(&intf->dev, data);
 }
 
+/* this maximum is arbitrary */
+#define USB_MAXINTERFACES	32
+
 /* USB_DT_CONFIG: Configuration descriptor information.
  *
  * USB_DT_OTHER_SPEED_CONFIG is the same descriptor, except that the
@@ -153,7 +156,7 @@
 	/* the interfaces associated with this configuration
 	 * these will be in numeric order, 0..desc.bNumInterfaces
 	 */
-	struct usb_interface *interface;
+	struct usb_interface *interface[USB_MAXINTERFACES];
 
 	unsigned char *extra;   /* Extra descriptors */
 	int extralen;
