CVE-2018-20169: Uncontrolled Resource Consumption (CWE-400)
value: An issue was discovered in the Linux kernel before 4.19.9. The USB subsystem mishandles size checks during the reading of an extra descriptor, related to __usb_get_extra_descriptor in drivers/usb/core/usb.c.
CWE description: The product does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed, eventually leading to the exhaustion of available resources.
CVSS v3 base score: 6.8
Published: 2018-12-17T07:29Z
Repository: https://github.com/torvalds/linux
Language: C
Files changed: drivers/usb/core/hub.c, drivers/usb/core/usb.c, drivers/usb/host/hwa-hc.c, include/linux/usb.h
Commit date: 2018-12-05 21:20:14 +0100
Commit message: USB: check usb_get_extra_descriptor for proper size

When reading an extra descriptor, we need to properly check the minimum
and maximum size allowed, to prevent from invalid data being sent by a
device.

Reported-by: Hui Peng <benquike@gmail.com>
Reported-by: Mathias Payer <mathias.payer@nebelwelt.net>
Co-developed-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Hui Peng <benquike@gmail.com>
Signed-off-by: Mathias Payer <mathias.payer@nebelwelt.net>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Diff: diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 528664059a12..f76b2e0aba9d 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2249,11 +2249,11 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
 		unsigned			port1 = udev->portnum;
 
 		/* descriptor may appear anywhere in config */
 		err = __usb_get_extra_descriptor(udev->rawdescriptors[0],
 				le16_to_cpu(udev->config[0].desc.wTotalLength),
-				USB_DT_OTG, (void **) &desc);
+				USB_DT_OTG, (void **) &desc, sizeof(*desc));
 		if (err || !(desc->bmAttributes & USB_OTG_HNP))
 			return 0;
 
 		dev_info(&udev->dev, "Dual-Role OTG device on %sHNP port\n",
 					(port1 == bus->otg_port) ? "" : "non-");
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 79d8bd7a612e..4ebfbd737905 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -830,27 +830,27 @@ EXPORT_SYMBOL_GPL(usb_get_current_frame_number);
  * __usb_get_extra_descriptor() finds a descriptor of specific type in the
  * extra field of the interface and endpoint descriptor structs.
  */
 
 int __usb_get_extra_descriptor(char *buffer, unsigned size,
-			       unsigned char type, void **ptr)
+			       unsigned char type, void **ptr, size_t minsize)
 {
 	struct usb_descriptor_header *header;
 
 	while (size >= sizeof(struct usb_descriptor_header)) {
 		header = (struct usb_descriptor_header *)buffer;
 
-		if (header->bLength < 2) {
+		if (header->bLength < 2 || header->bLength > size) {
 			printk(KERN_ERR
 				"%s: bogus descriptor, type %d length %d\n",
 				usbcore_name,
 				header->bDescriptorType,
 				header->bLength);
 			return -1;
 		}
 
-		if (header->bDescriptorType == type) {
+		if (header->bDescriptorType == type && header->bLength >= minsize) {
 			*ptr = header;
 			return 0;
 		}
 
 		buffer += header->bLength;
diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c
index 684d6f074c3a..09a8ebd95588 100644
--- a/drivers/usb/host/hwa-hc.c
+++ b/drivers/usb/host/hwa-hc.c
@@ -638,11 +638,11 @@ static int hwahc_security_create(struct hwahc *hwahc)
 	itr = usb_dev->rawdescriptors[index];
 	itr_size = le16_to_cpu(usb_dev->actconfig->desc.wTotalLength);
 	top = itr + itr_size;
 	result = __usb_get_extra_descriptor(usb_dev->rawdescriptors[index],
 			le16_to_cpu(usb_dev->actconfig->desc.wTotalLength),
-			USB_DT_SECURITY, (void **) &secd);
+			USB_DT_SECURITY, (void **) &secd, sizeof(*secd));
 	if (result == -1) {
 		dev_warn(dev, "BUG? WUSB host has no security descriptors\n");
 		return 0;
 	}
 	needed = sizeof(*secd);
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 4cdd515a4385..5e49e82c4368 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -405,15 +405,15 @@ struct usb_host_bos {
 	struct usb_ss_container_id_descriptor	*ss_id;
 	struct usb_ptm_cap_descriptor	*ptm_cap;
 };
 
 int __usb_get_extra_descriptor(char *buffer, unsigned size,
-	unsigned char type, void **ptr);
+	unsigned char type, void **ptr, size_t min);
 #define usb_get_extra_descriptor(ifpoint, type, ptr) \
 				__usb_get_extra_descriptor((ifpoint)->extra, \
 				(ifpoint)->extralen, \
-				type, (void **)ptr)
+				type, (void **)ptr, sizeof(**(ptr)))
 
 /* ----------------------------------------------------------------------- */
 
 /* USB device number allocation bitmap */
 struct usb_devmap {

Vulnerable code: 				USB_DT_OTG, (void **) &desc);
			       unsigned char type, void **ptr)
		if (header->bLength < 2) {
		if (header->bDescriptorType == type) {
			USB_DT_SECURITY, (void **) &secd);
	unsigned char type, void **ptr);
				type, (void **)ptr)
Fixed code: 				USB_DT_OTG, (void **) &desc, sizeof(*desc));
			       unsigned char type, void **ptr, size_t minsize)
		if (header->bLength < 2 || header->bLength > size) {
		if (header->bDescriptorType == type && header->bLength >= minsize) {
			USB_DT_SECURITY, (void **) &secd, sizeof(*secd));
	unsigned char type, void **ptr, size_t min);
				type, (void **)ptr, sizeof(**(ptr)))