| From oneukum@suse.com Tue Nov 28 10:13:45 2017 |
| From: Oliver Neukum <oneukum@suse.com> |
| Date: Thu, 23 Nov 2017 16:20:05 +0100 |
| Subject: USB: fix buffer overflows with parsing CDC headers |
| To: gregKH@linuxfoundation.org, linux-usb@vger.kernel.org, stable@kernel.org |
| Cc: Oliver Neukum <oneukum@suse.com> |
| Message-ID: <20171123152005.22493-1-oneukum@suse.com> |
| |
| From: Oliver Neukum <oneukum@suse.com> |
| |
| Parsing CDC headers a buffer overflow cannot just be prevented |
| by checking that the remainder of the buffer is longer than minimum |
| length. The size of the fields to be parsed must be figured in, too. |
| |
| In newer kernels this issue has been fixed at a central location with |
| |
| commit 2e1c42391ff2556387b3cb6308b24f6f65619feb |
| Author: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Date: Thu Sep 21 16:58:48 2017 +0200 |
| |
| USB: core: harden cdc_parse_cdc_header |
| |
| on anything older the parsing had not been centralised, so a separate |
| fix for each driver is necessary. |
| |
| Signed-off-by: Oliver Neukum <oneukum@suse.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/net/usb/cdc_ether.c | 9 ++++++++- |
| drivers/usb/class/cdc-acm.c | 2 +- |
| drivers/usb/class/cdc-wdm.c | 2 ++ |
| 3 files changed, 11 insertions(+), 2 deletions(-) |
| |
| --- a/drivers/net/usb/cdc_ether.c |
| +++ b/drivers/net/usb/cdc_ether.c |
| @@ -171,6 +171,8 @@ int usbnet_generic_cdc_bind(struct usbne |
| dev_dbg(&intf->dev, "extra CDC header\n"); |
| goto bad_desc; |
| } |
| + if (len < sizeof(struct usb_cdc_header_desc)) |
| + break; |
| info->header = (void *) buf; |
| if (info->header->bLength != sizeof(*info->header)) { |
| dev_dbg(&intf->dev, "CDC header len %u\n", |
| @@ -184,6 +186,8 @@ int usbnet_generic_cdc_bind(struct usbne |
| */ |
| if (rndis) { |
| struct usb_cdc_acm_descriptor *acm; |
| + if (len < sizeof(struct usb_cdc_acm_descriptor)) |
| + break; |
| |
| acm = (void *) buf; |
| if (acm->bmCapabilities) { |
| @@ -200,6 +204,8 @@ int usbnet_generic_cdc_bind(struct usbne |
| dev_dbg(&intf->dev, "extra CDC union\n"); |
| goto bad_desc; |
| } |
| + if (len < sizeof(struct usb_cdc_union_desc)) |
| + break; |
| info->u = (void *) buf; |
| if (info->u->bLength != sizeof(*info->u)) { |
| dev_dbg(&intf->dev, "CDC union len %u\n", |
| @@ -258,6 +264,8 @@ int usbnet_generic_cdc_bind(struct usbne |
| dev_dbg(&intf->dev, "extra CDC ether\n"); |
| goto bad_desc; |
| } |
| + if (len < sizeof(struct usb_cdc_ether_desc)) |
| + break; |
| info->ether = (void *) buf; |
| if (info->ether->bLength != sizeof(*info->ether)) { |
| dev_dbg(&intf->dev, "CDC ether len %u\n", |
| @@ -275,7 +283,6 @@ int usbnet_generic_cdc_bind(struct usbne |
| dev_dbg(&intf->dev, "extra MDLM descriptor\n"); |
| goto bad_desc; |
| } |
| - |
| desc = (void *)buf; |
| |
| if (desc->bLength != sizeof(*desc)) |
| --- a/drivers/usb/class/cdc-acm.c |
| +++ b/drivers/usb/class/cdc-acm.c |
| @@ -1139,7 +1139,7 @@ static int acm_probe(struct usb_interfac |
| } |
| } |
| |
| - while (buflen > 0) { |
| + while (buflen >= 3) { /* minimum length making sense */ |
| elength = buffer[0]; |
| if (!elength) { |
| dev_err(&intf->dev, "skipping garbage byte\n"); |
| --- a/drivers/usb/class/cdc-wdm.c |
| +++ b/drivers/usb/class/cdc-wdm.c |
| @@ -891,6 +891,8 @@ static int wdm_probe(struct usb_interfac |
| case USB_CDC_HEADER_TYPE: |
| break; |
| case USB_CDC_DMM_TYPE: |
| + if (buflen < sizeof(struct usb_cdc_dmm_desc)) |
| + break; |
| dmhd = (struct usb_cdc_dmm_desc *)buffer; |
| maxcom = le16_to_cpu(dmhd->wMaxCommand); |
| dev_dbg(&intf->dev, |