CVE-2017-16531: Improper Restriction of Operations within the Bounds of a Memory Buffer (CWE-119)
value: drivers/usb/core/config.c in the Linux kernel before 4.13.6 allows local users to cause a denial of service (out-of-bounds read and system crash) or possibly have unspecified other impact via a crafted USB device, related to the USB_DT_INTERFACE_ASSOCIATION descriptor.
CWE description: The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
CVSS v3 base score: 6.6
Published: 2017-11-04T01:29Z
Repository: https://github.com/torvalds/linux
Language: C
Files changed: drivers/usb/core/config.c, include/uapi/linux/usb/ch9.h
Commit date: 2017-09-19 17:27:16 +0200
Commit message: USB: fix out-of-bounds in usb_set_configuration

Andrey Konovalov reported a possible out-of-bounds problem for a USB interface
association descriptor.  He writes:
	It seems there's no proper size check of a USB_DT_INTERFACE_ASSOCIATION
	descriptor. It's only checked that the size is >= 2 in
	usb_parse_configuration(), so find_iad() might do out-of-bounds access
	to intf_assoc->bInterfaceCount.

And he's right, we don't check for crazy descriptors of this type very well, so
resolve this problem.  Yet another issue found by syzkaller...

Reported-by: Andrey Konovalov <andreyknvl@google.com>
Tested-by: Andrey Konovalov <andreyknvl@google.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Diff: diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 854c8d66cfbefa..68b54bd88d1eb0 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -641,19 +641,27 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
 				++n;
 			}
 
 		} else if (header->bDescriptorType ==
 				USB_DT_INTERFACE_ASSOCIATION) {
+			struct usb_interface_assoc_descriptor *d;
+
+			d = (struct usb_interface_assoc_descriptor *)header;
+			if (d->bLength < USB_DT_INTERFACE_ASSOCIATION_SIZE) {
+				dev_warn(ddev,
+					 "config %d has an invalid interface association descriptor of length %d, skipping\n",
+					 cfgno, d->bLength);
+				continue;
+			}
+
 			if (iad_num == USB_MAXIADS) {
 				dev_warn(ddev, "found more Interface "
 					       "Association Descriptors "
 					       "than allocated for in "
 					       "configuration %d\n", cfgno);
 			} else {
-				config->intf_assoc[iad_num] =
-					(struct usb_interface_assoc_descriptor
-					*)header;
+				config->intf_assoc[iad_num] = d;
 				iad_num++;
 			}
 
 		} else if (header->bDescriptorType == USB_DT_DEVICE ||
 			    header->bDescriptorType == USB_DT_CONFIG)
diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h
index ce1169af39d72c..2a5d63040a0b0c 100644
--- a/include/uapi/linux/usb/ch9.h
+++ b/include/uapi/linux/usb/ch9.h
@@ -778,10 +778,11 @@ struct usb_interface_assoc_descriptor {
 	__u8  bFunctionSubClass;
 	__u8  bFunctionProtocol;
 	__u8  iFunction;
 } __attribute__ ((packed));
 
+#define USB_DT_INTERFACE_ASSOCIATION_SIZE	8
 
 /*-------------------------------------------------------------------------*/
 
 /* USB_DT_SECURITY:  group of wireless security descriptors, including
  * encryption types available for setting up a CC/association.

Vulnerable code: 				config->intf_assoc[iad_num] =
					(struct usb_interface_assoc_descriptor
					*)header;
Fixed code: 			struct usb_interface_assoc_descriptor *d;

			d = (struct usb_interface_assoc_descriptor *)header;
			if (d->bLength < USB_DT_INTERFACE_ASSOCIATION_SIZE) {
				dev_warn(ddev,
					 "config %d has an invalid interface association descriptor of length %d, skipping\n",
					 cfgno, d->bLength);
				continue;
			}

				config->intf_assoc[iad_num] = d;
#define USB_DT_INTERFACE_ASSOCIATION_SIZE	8