| From: Oliver Neukum <oneukum@suse.de> |
| Date: Wed, 28 Jan 2015 11:14:55 +0100 |
| Subject: 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> |
| --- |
| drivers/usb/class/cdc-acm.c | 21 ++++++++++++++++----- |
| 1 file changed, 16 insertions(+), 5 deletions(-) |
| |
| --- a/drivers/usb/class/cdc-acm.c |
| +++ b/drivers/usb/class/cdc-acm.c |
| @@ -911,6 +911,7 @@ static int acm_probe(struct usb_interfac |
| 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_interfac |
| 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_interfac |
| 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) { |