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;