One of the non-checked value read from an untrusted descriptor is the
"maximum packet size" of an endpoint. If a device reports an incorrect
value most of our HC drivers wont work and if this value is 0 ehci(4)
will crash the kernel.
So here's a diff to validate the value read from the device descriptor
which ends up being the value of the default endpoint.
ok?
Index: usb_subr.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/usb_subr.c,v
retrieving revision 1.128
diff -u -p -r1.128 usb_subr.c
--- usb_subr.c 12 Sep 2016 07:43:10 -0000 1.128
+++ usb_subr.c 17 Sep 2016 13:38:02 -0000
@@ -1023,16 +1027,34 @@ usbd_status
usbd_new_device(struct device *parent, struct usbd_bus *bus, int depth,
int speed, int port, struct usbd_port *up)
{
- struct usbd_device *dev, *adev;
- struct usbd_device *hub;
+ struct usbd_device *dev, *adev, *hub;
usb_device_descriptor_t *dd;
usbd_status err;
- int addr;
- int i;
- int p;
+ uint32_t mps, mps0;
+ int addr, i, p;
DPRINTF(("usbd_new_device bus=%p port=%d depth=%d speed=%d\n",
bus, port, depth, speed));
+
+ /*
+ * Fixed size for ep0 max packet, FULL device variable size is
+ * handled below.
+ */
+ switch (speed) {
+ case USB_SPEED_LOW:
+ mps0 = 8;
+ break;
+ case USB_SPEED_HIGH:
+ case USB_SPEED_FULL:
+ mps0 = 64;
+ break;
+ case USB_SPEED_SUPER:
+ mps0 = 512;
+ break;
+ default:
+ return (USBD_INVAL);
+ }
+
addr = usbd_getnewaddr(bus);
if (addr < 0) {
printf("%s: No free USB addresses, new device ignored.\n",
@@ -1054,8 +1076,8 @@ usbd_new_device(struct device *parent, s
dev->def_ep_desc.bDescriptorType = UDESC_ENDPOINT;
dev->def_ep_desc.bEndpointAddress = USB_CONTROL_ENDPOINT;
dev->def_ep_desc.bmAttributes = UE_CONTROL;
- USETW(dev->def_ep_desc.wMaxPacketSize, USB_MAX_IPACKET);
dev->def_ep_desc.bInterval = 0;
+ USETW(dev->def_ep_desc.wMaxPacketSize, mps0);
dev->quirks = &usbd_no_quirk;
dev->address = USB_START_ADDR;
@@ -1063,6 +1085,8 @@ usbd_new_device(struct device *parent, s
dev->depth = depth;
dev->powersrc = up;
dev->myhub = up->parent;
+ dev->speed = speed;
+ dev->langid = USBD_NOLANG;
up->device = dev;
@@ -1084,8 +1108,6 @@ usbd_new_device(struct device *parent, s
} else {
dev->myhsport = NULL;
}
- dev->speed = speed;
- dev->langid = USBD_NOLANG;
/* Establish the default pipe. */
err = usbd_setup_pipe(dev, 0, &dev->def_ep, USBD_DEFAULT_INTERVAL,
@@ -1143,36 +1165,33 @@ usbd_new_device(struct device *parent, s
return (err);
}
- if (speed == USB_SPEED_HIGH) {
- /* Max packet size must be 64 (sec 5.5.3). */
- if (dd->bMaxPacketSize != USB_2_MAX_CTRL_PACKET) {
-#ifdef DIAGNOSTIC
- printf("%s: addr=%d bad max packet size %d\n", __func__,
- addr, dd->bMaxPacketSize);
-#endif
- dd->bMaxPacketSize = USB_2_MAX_CTRL_PACKET;
- }
- }
-
DPRINTF(("usbd_new_device: adding unit addr=%d, rev=%02x, class=%d, "
"subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n",
addr,UGETW(dd->bcdUSB), dd->bDeviceClass, dd->bDeviceSubClass,
dd->bDeviceProtocol, dd->bMaxPacketSize, dd->bLength,
dev->speed));
- if (dd->bDescriptorType != UDESC_DEVICE) {
+ if ((dd->bDescriptorType != UDESC_DEVICE) ||
+ (dd->bLength < USB_DEVICE_DESCRIPTOR_SIZE)) {
usb_free_device(dev);
up->device = NULL;
return (USBD_INVAL);
}
- if (dd->bLength < USB_DEVICE_DESCRIPTOR_SIZE) {
- usb_free_device(dev);
- up->device = NULL;
- return (USBD_INVAL);
+ mps = dd->bMaxPacketSize;
+ if (speed == USB_SPEED_SUPER && mps == 0xff)
+ mps = 512;
+
+ if (mps != mps0) {
+ if ((speed == USB_SPEED_LOW) ||
+ (mps != 8 || mps != 16 || mps != 32 || mps != 64)) {
+ usb_free_device(dev);
+ up->device = NULL;
+ return (USBD_INVAL);
+ }
+ USETW(dev->def_ep_desc.wMaxPacketSize, mps);
}
- USETW(dev->def_ep_desc.wMaxPacketSize, dd->bMaxPacketSize);
/* Set the address if the HC didn't do it already. */
if (bus->methods->dev_setaddr != NULL &&