diff options
| -rw-r--r-- | drivers/usb/core/config.c | 89 | ||||
| -rw-r--r-- | include/linux/usb.h | 2 |
2 files changed, 54 insertions, 37 deletions
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 89112f2bde2f..4ce45e0091d7 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -10,7 +10,6 @@ /* these maximums are arbitrary */ #define USB_MAXCONFIG 8 -#define USB_ALTSETTINGALLOC 4 #define USB_MAXINTERFACES 32 static int usb_parse_endpoint(struct usb_host_endpoint *endpoint, unsigned char *buffer, int size) @@ -108,43 +107,27 @@ static int usb_parse_interface(struct usb_interface *interface, unsigned char *b struct usb_host_interface *ifp; unsigned char *begin; - interface->max_altsetting = USB_ALTSETTINGALLOC; - interface->altsetting = kmalloc(sizeof(*interface->altsetting) * interface->max_altsetting, - GFP_KERNEL); - if (!interface->altsetting) { - err("couldn't kmalloc interface->altsetting"); - return -ENOMEM; - } - - while (size > 0) { + ifp = interface->altsetting; + while (size >= sizeof(struct usb_descriptor_header)) { struct usb_interface_descriptor *d; - if (interface->num_altsetting >= interface->max_altsetting) { - struct usb_host_interface *ptr; - int oldmas; + d = (struct usb_interface_descriptor *) buffer; + if (d->bAlternateSetting >= interface->num_altsetting) { - oldmas = interface->max_altsetting; - interface->max_altsetting += USB_ALTSETTINGALLOC; - if (interface->max_altsetting > USB_MAXALTSETTING) { - warn("too many alternate settings (incr %d max %d)\n", - USB_ALTSETTINGALLOC, USB_MAXALTSETTING); - return -EINVAL; - } + /* Skip to the next interface descriptor */ + buffer += d->bLength; + size -= d->bLength; + while (size >= sizeof(struct usb_descriptor_header)) { + header = (struct usb_descriptor_header *) buffer; - ptr = kmalloc(sizeof(*ptr) * interface->max_altsetting, GFP_KERNEL); - if (ptr == NULL) { - err("couldn't kmalloc interface->altsetting"); - return -ENOMEM; + if (header->bDescriptorType == USB_DT_INTERFACE) + break; + buffer += header->bLength; + size -= header->bLength; } - memcpy(ptr, interface->altsetting, sizeof(*interface->altsetting) * oldmas); - kfree(interface->altsetting); - interface->altsetting = ptr; + continue; } - ifp = interface->altsetting + interface->num_altsetting; - memset(ifp, 0, sizeof(*ifp)); - interface->num_altsetting++; - memcpy(&ifp->desc, buffer, USB_DT_INTERFACE_SIZE); buffer += ifp->desc.bLength; @@ -216,6 +199,8 @@ static int usb_parse_interface(struct usb_interface *interface, unsigned char *b || d->bDescriptorType != USB_DT_INTERFACE || !d->bAlternateSetting) break; + + ++ifp; } return buffer - buffer0; @@ -223,7 +208,7 @@ static int usb_parse_interface(struct usb_interface *interface, unsigned char *b int usb_parse_configuration(struct usb_host_config *config, char *buffer) { - int nintf; + int nintf, nintf_orig; int i, j, size; struct usb_interface *interface; char *buffer2; @@ -237,7 +222,7 @@ int usb_parse_configuration(struct usb_host_config *config, char *buffer) le16_to_cpus(&config->desc.wTotalLength); size = config->desc.wTotalLength; - nintf = config->desc.bNumInterfaces; + nintf = nintf_orig = config->desc.bNumInterfaces; if (nintf > USB_MAXINTERFACES) { warn("too many interfaces (%d max %d)", nintf, USB_MAXINTERFACES); @@ -260,7 +245,8 @@ int usb_parse_configuration(struct usb_host_config *config, char *buffer) get_device(&interface->dev); } - /* Go through the descriptors, checking their length */ + /* Go through the descriptors, checking their length and counting the + * number of altsettings for each interface */ buffer2 = buffer; size2 = size; j = 0; @@ -272,10 +258,21 @@ int usb_parse_configuration(struct usb_host_config *config, char *buffer) } if (header->bDescriptorType == USB_DT_INTERFACE) { + struct usb_interface_descriptor *d; + if (header->bLength < USB_DT_INTERFACE_SIZE) { warn("invalid interface descriptor"); return -EINVAL; } + d = (struct usb_interface_descriptor *) header; + i = d->bInterfaceNumber; + if (i >= nintf_orig) { + warn("invalid interface number (%d/%d)", + i, nintf_orig); + return -EINVAL; + } + if (i < nintf) + ++config->interface[i]->num_altsetting; } else if ((header->bDescriptorType == USB_DT_DEVICE || header->bDescriptorType == USB_DT_CONFIG) && j) { @@ -288,6 +285,28 @@ int usb_parse_configuration(struct usb_host_config *config, char *buffer) size2 -= header->bLength; } + /* Allocate the altsetting arrays */ + for (i = 0; i < config->desc.bNumInterfaces; ++i) { + interface = config->interface[i]; + if (interface->num_altsetting > USB_MAXALTSETTING) { + warn("too many alternate settings for interface %d (%d max %d)\n", + i, interface->num_altsetting, USB_MAXALTSETTING); + return -EINVAL; + } + if (interface->num_altsetting == 0) { + warn("no alternate settings for interface %d", i); + return -EINVAL; + } + + len = sizeof(*interface->altsetting) * interface->num_altsetting; + interface->altsetting = kmalloc(len, GFP_KERNEL); + if (!interface->altsetting) { + err("couldn't kmalloc interface->altsetting"); + return -ENOMEM; + } + memset(interface->altsetting, 0, len); + } + buffer += config->desc.bLength; size -= config->desc.bLength; @@ -325,7 +344,7 @@ int usb_parse_configuration(struct usb_host_config *config, char *buffer) } /* Parse all the interface/altsetting descriptors */ - for (i = 0; i < nintf; i++) { + for (i = 0; i < nintf_orig; i++) { retval = usb_parse_interface(config->interface[i], buffer, size); if (retval < 0) return retval; diff --git a/include/linux/usb.h b/include/linux/usb.h index a20b33d953aa..5575ebfb276d 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -80,7 +80,6 @@ struct usb_host_interface { * @act_altsetting: index of current altsetting. this number is always * less than num_altsetting. after the device is configured, each * interface uses its default setting of zero. - * @max_altsetting: the max number of altsettings for this interface. * @driver: the USB driver that is bound to this interface. * @minor: the minor number assigned to this interface, if this * interface is bound to a driver that uses the USB major number. @@ -118,7 +117,6 @@ struct usb_interface { unsigned act_altsetting; /* active alternate setting */ unsigned num_altsetting; /* number of alternate settings */ - unsigned max_altsetting; /* total memory allocated */ struct usb_driver *driver; /* driver */ int minor; /* minor number this interface is bound to */ |
