[PATCH] [stable only]USB: fix buffer overflows with parsing CDC headers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxxxxxxxxxx>
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@xxxxxxxx>
---
 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(-)

diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index d3920b54a92c..579500d69757 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -171,6 +171,8 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
 				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 usbnet *dev, struct usb_interface *intf)
 			 */
 			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 usbnet *dev, struct usb_interface *intf)
 				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 usbnet *dev, struct usb_interface *intf)
 				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 usbnet *dev, struct usb_interface *intf)
 				dev_dbg(&intf->dev, "extra MDLM descriptor\n");
 				goto bad_desc;
 			}
-
 			desc = (void *)buf;
 
 			if (desc->bLength != sizeof(*desc))
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index d67484b3dcfa..a5e94b8fa2dc 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1139,7 +1139,7 @@ static int acm_probe(struct usb_interface *intf,
 		}
 	}
 
-	while (buflen > 0) {
+	while (buflen >= 3) { /* minimum length making sense */
 		elength = buffer[0];
 		if (!elength) {
 			dev_err(&intf->dev, "skipping garbage byte\n");
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index a81f9dd7ee97..df0878c4810c 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -891,6 +891,8 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
 		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,
-- 
2.13.6

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux