While analysing okan@'s NULL-dereference in ugen_do_clos() I could not
convince myself that usbd_set_interface() was not modifying `iface' if
usbd_fill_iface_data() failed.

So I de-obfuscate usbd_fill_iface_data() as below.  As a bonus this add
various sizes to free(9) and change a usbd_status -> int because there's
no I/O going on here!

Works for me, more tests with ugen(4) and reviews welcome.

Index: usb_subr.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/usb_subr.c,v
retrieving revision 1.117
diff -u -p -r1.117 usb_subr.c
--- usb_subr.c  23 Mar 2015 22:26:01 -0000      1.117
+++ usb_subr.c  14 Oct 2015 09:44:34 -0000
@@ -416,10 +416,6 @@ usbd_find_idesc(usb_config_descriptor_t 
 
        for (curidx = lastidx = -1; p < end; ) {
                d = (usb_interface_descriptor_t *)p;
-               DPRINTFN(4,("usbd_find_idesc: idx=%d(%d) altidx=%d(%d) len=%d "
-                           "type=%d\n",
-                           ifaceidx, curidx, altidx, curaidx,
-                           d->bLength, d->bDescriptorType));
                if (d->bLength == 0) /* bad descriptor */
                        break;
                p += d->bLength;
@@ -470,102 +466,102 @@ usbd_find_edesc(usb_config_descriptor_t 
        return (NULL);
 }
 
-usbd_status
+static inline usb_endpoint_descriptor_t *
+usbd_fix_ed(struct usbd_device *dev, usb_endpoint_descriptor_t *ed)
+{
+       uint32_t mps;
+
+       if (dev->speed == USB_SPEED_HIGH) {
+               /* Control and bulk endpoints have max packet limits. */
+               switch (UE_GET_XFERTYPE(ed->bmAttributes)) {
+               case UE_CONTROL:
+                       mps = USB_2_MAX_CTRL_PACKET;
+                       goto check;
+               case UE_BULK:
+                       mps = USB_2_MAX_BULK_PACKET;
+               check:
+                       if (UGETW(ed->wMaxPacketSize) != mps) {
+                               USETW(ed->wMaxPacketSize, mps);
+#ifdef DIAGNOSTIC
+                               printf("%s: bad max packet size\n", __func__);
+#endif
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       return (ed);
+}
+
+int
 usbd_fill_iface_data(struct usbd_device *dev, int ifaceidx, int altidx)
 {
-       struct usbd_interface *ifc = &dev->ifaces[ifaceidx];
-       usb_interface_descriptor_t *idesc;
+       struct usbd_interface *iface = &dev->ifaces[ifaceidx];
+       usb_interface_descriptor_t *id;
+       usb_endpoint_descriptor_t *ed;
+       struct usbd_endpoint *endpoints = NULL;
        char *p, *end;
-       int endpt, nendpt;
+       int endpt;
+
+       id = usbd_find_idesc(dev->cdesc, ifaceidx, altidx);
+       if (id == NULL)
+               return (-1);
 
-       DPRINTFN(4,("usbd_fill_iface_data: ifaceidx=%d altidx=%d\n",
-                   ifaceidx, altidx));
-       idesc = usbd_find_idesc(dev->cdesc, ifaceidx, altidx);
-       if (idesc == NULL)
-               return (USBD_INVAL);
-       ifc->device = dev;
-       ifc->idesc = idesc;
-       ifc->index = ifaceidx;
-       ifc->altindex = altidx;
-       nendpt = ifc->idesc->bNumEndpoints;
-       DPRINTFN(4,("usbd_fill_iface_data: found idesc nendpt=%d\n", nendpt));
-       if (nendpt != 0) {
-               ifc->endpoints = mallocarray(nendpt,
-                   sizeof(struct usbd_endpoint), M_USB, M_NOWAIT);
-               if (ifc->endpoints == NULL)
-                       return (USBD_NOMEM);
-       } else
-               ifc->endpoints = NULL;
-       ifc->priv = NULL;
-       p = (char *)ifc->idesc + ifc->idesc->bLength;
+       if (id->bNumEndpoints != 0)
+               endpoints = mallocarray(id->bNumEndpoints, sizeof(*endpoints),
+                   M_USB, M_WAITOK);
+
+       p = (char *)id + id->bLength;
        end = (char *)dev->cdesc + UGETW(dev->cdesc->wTotalLength);
-#define ed ((usb_endpoint_descriptor_t *)p)
-       for (endpt = 0; endpt < nendpt; endpt++) {
-               DPRINTFN(10,("usbd_fill_iface_data: endpt=%d\n", endpt));
-               for (; p < end; p += ed->bLength) {
-                       DPRINTFN(10,("usbd_fill_iface_data: p=%p end=%p "
-                           "len=%d type=%d\n", p, end, ed->bLength,
-                           ed->bDescriptorType));
-                       if (p + ed->bLength <= end && ed->bLength != 0 &&
-                           ed->bDescriptorType == UDESC_ENDPOINT)
-                               goto found;
+
+       for (endpt = 0; endpt < id->bNumEndpoints; endpt++) {
+               while (p < end) {
+                       ed = (usb_endpoint_descriptor_t *)p;
+
                        if (ed->bLength == 0 ||
                            ed->bDescriptorType == UDESC_INTERFACE)
                                break;
+
+                       if (p + ed->bLength <= end &&
+                           ed->bDescriptorType == UDESC_ENDPOINT) {
+                               endpoints[endpt].edesc = usbd_fix_ed(dev, ed);
+                               endpoints[endpt].refcnt = 0;
+                               endpoints[endpt].savedtoggle = 0;
+                               goto found;
+                       }
+
+                       p += ed->bLength;
                }
+
                /* passed end, or bad desc */
-               printf("usbd_fill_iface_data: bad descriptor(s): %s\n",
-                   ed->bLength == 0 ? "0 length" :
-                   ed->bDescriptorType == UDESC_INTERFACE ? "iface desc" :
-                   "out of data");
-               goto bad;
+               free(endpoints, M_USB, id->bNumEndpoints * sizeof(*endpoints));
+               return (-1);
+
        found:
-               ifc->endpoints[endpt].edesc = ed;
-               if (dev->speed == USB_SPEED_HIGH) {
-                       u_int mps;
-                       /* Control and bulk endpoints have max packet
-                          limits. */
-                       switch (UE_GET_XFERTYPE(ed->bmAttributes)) {
-                       case UE_CONTROL:
-                               mps = USB_2_MAX_CTRL_PACKET;
-                               goto check;
-                       case UE_BULK:
-                               mps = USB_2_MAX_BULK_PACKET;
-                       check:
-                               if (UGETW(ed->wMaxPacketSize) != mps) {
-                                       USETW(ed->wMaxPacketSize, mps);
-#ifdef DIAGNOSTIC
-                                       printf("usbd_fill_iface_data: bad max "
-                                           "packet size\n");
-#endif
-                               }
-                               break;
-                       default:
-                               break;
-                       }
-               }
-               ifc->endpoints[endpt].refcnt = 0;
-               ifc->endpoints[endpt].savedtoggle = 0;
                p += ed->bLength;
        }
-#undef ed
-       LIST_INIT(&ifc->pipes);
-       return (USBD_NORMAL_COMPLETION);
-
- bad:
-       if (ifc->endpoints != NULL) {
-               free(ifc->endpoints, M_USB, 0);
-               ifc->endpoints = NULL;
-       }
-       return (USBD_INVAL);
+
+       iface->device = dev;
+       iface->idesc = id;
+       iface->index = ifaceidx;
+       iface->altindex = altidx;
+       iface->endpoints = endpoints;
+       iface->priv = NULL;
+       LIST_INIT(&iface->pipes);
+       iface->claimed = 0;
+
+       return (0);
 }
 
 void
 usbd_free_iface_data(struct usbd_device *dev, int ifcno)
 {
-       struct usbd_interface *ifc = &dev->ifaces[ifcno];
-       if (ifc->endpoints)
-               free(ifc->endpoints, M_USB, 0);
+       struct usbd_interface *iface = &dev->ifaces[ifcno];
+       int nendpt = iface->idesc->bNumEndpoints;
+
+       free(iface->endpoints, M_USB, nendpt * sizeof(*iface->endpoints));
 }
 
 usbd_status
Index: usbdi.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/usbdi.c,v
retrieving revision 1.83
diff -u -p -r1.83 usbdi.c
--- usbdi.c     10 Jul 2015 15:47:48 -0000      1.83
+++ usbdi.c     13 Oct 2015 20:58:02 -0000
@@ -629,20 +629,20 @@ usbd_status
 usbd_set_interface(struct usbd_interface *iface, int altidx)
 {
        usb_device_request_t req;
-       usbd_status err;
-       void *endpoints;
+       struct usbd_endpoint *endpoints;
+       int nendpt;
 
        if (LIST_FIRST(&iface->pipes) != 0)
                return (USBD_IN_USE);
 
        endpoints = iface->endpoints;
-       err = usbd_fill_iface_data(iface->device, iface->index, altidx);
-       if (err)
-               return (err);
+       nendpt = iface->idesc->bNumEndpoints;
+
+       if (usbd_fill_iface_data(iface->device, iface->index, altidx))
+               return (USBD_INVAL);
 
        /* new setting works, we can free old endpoints */
-       if (endpoints != NULL)
-               free(endpoints, M_USB, 0);
+       free(endpoints, M_USB, nendpt * sizeof(*endpoints));
 
 #ifdef DIAGNOSTIC
        if (iface->idesc == NULL) {
Index: usbdivar.h
===================================================================
RCS file: /cvs/src/sys/dev/usb/usbdivar.h,v
retrieving revision 1.69
diff -u -p -r1.69 usbdivar.h
--- usbdivar.h  21 Dec 2014 12:04:01 -0000      1.69
+++ usbdivar.h  13 Oct 2015 20:51:32 -0000
@@ -238,7 +238,7 @@ usbd_status usbd_setup_pipe(struct usbd_
 int            usbd_set_address(struct usbd_device *, int);
 usbd_status    usbd_new_device(struct device *, struct usbd_bus *,
                    int, int, int, struct usbd_port *);
-usbd_status    usbd_fill_iface_data(struct usbd_device *, int, int);
+int            usbd_fill_iface_data(struct usbd_device *, int, int);
 
 usbd_status    usb_insert_transfer(struct usbd_xfer *);
 void           usb_transfer_complete(struct usbd_xfer *);

Reply via email to