diff options
author | Oliver Neukum <oneukum@suse.de> | 2015-01-28 11:14:55 +0100 |
---|---|---|
committer | Ben Hutchings <ben@decadent.org.uk> | 2015-05-09 23:16:14 +0100 |
commit | 5b013a6d6593cd1c6a5b8e27b0bd11765b547896 (patch) | |
tree | 8ea602adbc9980f32514f62f2f3ac636136a29a3 /drivers/usb/class | |
parent | 8801366e8d331dc84697211ea325fee207787be1 (diff) | |
download | kernel_samsung_smdk4412-5b013a6d6593cd1c6a5b8e27b0bd11765b547896.zip kernel_samsung_smdk4412-5b013a6d6593cd1c6a5b8e27b0bd11765b547896.tar.gz kernel_samsung_smdk4412-5b013a6d6593cd1c6a5b8e27b0bd11765b547896.tar.bz2 |
cdc-acm: add sanity checks
commit 7e860a6e7aa62b337a61110430cd633db5b0d2dd upstream.
Check the special CDC headers for a plausible minimum length.
Another big operating systems ignores such garbage.
Signed-off-by: Oliver Neukum <oneukum@suse.de>
Reviewed-by: Adam Lee <adam8157@gmail.com>
Tested-by: Adam Lee <adam8157@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
[bwh: Backported to 3.2: adjust context]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Diffstat (limited to 'drivers/usb/class')
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 21 |
1 files changed, 16 insertions, 5 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index d38d88e..360ddb5 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -911,6 +911,7 @@ static int acm_probe(struct usb_interface *intf, unsigned long quirks; int num_rx_buf; int i; + unsigned int elength = 0; int combined_interfaces = 0; /* normal quirks */ @@ -950,9 +951,12 @@ static int acm_probe(struct usb_interface *intf, dev_err(&intf->dev, "skipping garbage\n"); goto next_desc; } + elength = buffer[0]; switch (buffer[2]) { case USB_CDC_UNION_TYPE: /* we've found it */ + if (elength < sizeof(struct usb_cdc_union_desc)) + goto next_desc; if (union_header) { dev_err(&intf->dev, "More than one " "union descriptor, skipping ...\n"); @@ -961,31 +965,38 @@ static int acm_probe(struct usb_interface *intf, union_header = (struct usb_cdc_union_desc *)buffer; break; case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/ + if (elength < sizeof(struct usb_cdc_country_functional_desc)) + goto next_desc; cfd = (struct usb_cdc_country_functional_desc *)buffer; break; case USB_CDC_HEADER_TYPE: /* maybe check version */ break; /* for now we ignore it */ case USB_CDC_ACM_TYPE: + if (elength < 4) + goto next_desc; ac_management_function = buffer[3]; break; case USB_CDC_CALL_MANAGEMENT_TYPE: + if (elength < 5) + goto next_desc; call_management_function = buffer[3]; call_interface_num = buffer[4]; if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3) dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n"); break; default: - /* there are LOTS more CDC descriptors that + /* + * there are LOTS more CDC descriptors that * could legitimately be found here. */ dev_dbg(&intf->dev, "Ignoring descriptor: " - "type %02x, length %d\n", - buffer[2], buffer[0]); + "type %02x, length %ud\n", + buffer[2], elength); break; } next_desc: - buflen -= buffer[0]; - buffer += buffer[0]; + buflen -= elength; + buffer += elength; } if (!union_header) { |