add new usbd_do_request_len() that can allocate a larger than request size buffer. reimplement usbd_do_request_flags() in terms of this. use this for fetching string descriptors. fixes a very strange problem where an axe(4) attaching (either has ugen(4) or axe(4)) would ask for 2 bytes, usb_mem.c would allocate a 2 byte fragment, perform the operation, and sometime shortly afterwards (usually by the time the next allocation is made for this fragment), would become corrupted (usually two bytes were written with 0x0304.) (initial request of 4 bytes also avoids the problem on this device. it really seems like a HC problem -- host should not allow the device to write more than req.wLength!) Index: usb_subr.c =================================================================== RCS file: /cvsroot/src/sys/dev/usb/usb_subr.c,v retrieving revision 1.238 diff -p -u -r1.238 usb_subr.c --- usb_subr.c 21 Aug 2019 10:48:37 -0000 1.238 +++ usb_subr.c 26 Aug 2019 01:20:16 -0000 @@ -122,13 +122,20 @@ usbd_get_string_desc(struct usbd_device usbd_status err; int actlen; + /* + * Pass a full-sized buffer to usbd_do_request_len(). At least + * one device has been seen returning additional data beyond the + * provided buffers (2-bytes written shortly after the request + * claims to have completed and returned the 2 byt header, + * corrupting other memory. + */ req.bmRequestType = UT_READ_DEVICE; req.bRequest = UR_GET_DESCRIPTOR; USETW2(req.wValue, UDESC_STRING, sindex); USETW(req.wIndex, langid); USETW(req.wLength, 2); /* only size byte first */ - err = usbd_do_request_flags(dev, &req, sdesc, USBD_SHORT_XFER_OK, - &actlen, USBD_DEFAULT_TIMEOUT); + err = usbd_do_request_len(dev, &req, sizeof *sdesc, sdesc, + USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT); if (err) return err; @@ -138,8 +145,8 @@ usbd_get_string_desc(struct usbd_device if (sdesc->bLength > sizeof(*sdesc)) return USBD_INVAL; USETW(req.wLength, sdesc->bLength); /* the whole string */ - err = usbd_do_request_flags(dev, &req, sdesc, USBD_SHORT_XFER_OK, - &actlen, USBD_DEFAULT_TIMEOUT); + err = usbd_do_request_len(dev, &req, sizeof *sdesc, sdesc, + USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT); if (err) return err; Index: usbdi.c =================================================================== RCS file: /cvsroot/src/sys/dev/usb/usbdi.c,v retrieving revision 1.185 diff -p -u -r1.185 usbdi.c --- usbdi.c 24 Aug 2019 07:43:00 -0000 1.185 +++ usbdi.c 26 Aug 2019 01:20:16 -0000 @@ -1100,9 +1100,19 @@ usbd_status usbd_do_request_flags(struct usbd_device *dev, usb_device_request_t *req, void *data, uint16_t flags, int *actlen, uint32_t timeout) { + size_t len = UGETW(req->wLength); + + return usbd_do_request_len(dev, req, len, data, flags, actlen, timeout); +} + +usbd_status +usbd_do_request_len(struct usbd_device *dev, usb_device_request_t *req, + size_t len, void *data, uint16_t flags, int *actlen, uint32_t timeout) +{ struct usbd_xfer *xfer; usbd_status err; - size_t len = UGETW(req->wLength); + + KASSERT(len >= UGETW(req->wLength)); USBHIST_FUNC(); USBHIST_CALLARGS(usbdebug, "dev=%#jx req=%jx flgas=%jx len=%jx", Index: usbdi.h =================================================================== RCS file: /cvsroot/src/sys/dev/usb/usbdi.h,v retrieving revision 1.96 diff -p -u -r1.96 usbdi.h --- usbdi.h 27 Jan 2019 02:08:42 -0000 1.96 +++ usbdi.h 26 Aug 2019 01:20:16 -0000 @@ -143,6 +143,9 @@ usbd_status usbd_request_async(struct us usb_device_request_t *, void *, usbd_callback); usbd_status usbd_do_request_flags(struct usbd_device *, usb_device_request_t *, void *, uint16_t, int *, uint32_t); +usbd_status usbd_do_request_len(struct usbd_device *dev, + usb_device_request_t *req, size_t len, void *data, uint16_t flags, + int *actlen, uint32_t timeout); usb_interface_descriptor_t * usbd_get_interface_descriptor(struct usbd_interface *);