hub_control() sized the Greybus operation's response buffer purely off the caller-supplied wLength. However, per the USB hub class spec, GetHubDescriptor, GetHubStatus, and GetPortStatus have a response length that is fixed by the request type itself, and can be larger than (or independent of) whatever wLength the USB core happens to pass down. This could under-allocate the response buffer for these request types.
Special-case these three request types so the response buffer is always large enough for the data the module will actually send back, matching the equivalent handling in usbcore's rh_call_control(). Fall back to wLength for all other request types, as before.
Compile-tested with 'make M=drivers/staging/greybus C=1', including sparse, with no warnings. checkpatch --strict is also clean.
Note: this driver's hub_control() is currently unreachable at runtime since gb_usb_probe() unconditionally disables USB support pending separate USB core changes. As Project Ara hardware is no longer available, this change has not been tested on physical hardware or with a Greybus module; it was verified only by compilation, sparse, and code review against the equivalent logic in usbcore.
Signed-off-by: Aditya Chari S adi25charis@gmail.com --- drivers/staging/greybus/usb.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-)
diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index 475f24f20..ea6c0800c 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -105,8 +105,29 @@ static int hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, size_t response_size; int ret;
- /* FIXME: handle unspecified lengths */ - response_size = sizeof(*response) + wLength; + /* + * Several USB hub class requests have a response length that is + * fixed by the request type rather than by wLength (which may be + * zero, or otherwise smaller than the actual data the module will + * return). Make sure we always allocate enough room for those. + */ + switch (typeReq) { + case GetHubDescriptor: + response_size = sizeof(*response) + + sizeof(struct usb_hub_descriptor); + break; + case GetHubStatus: + response_size = sizeof(*response) + + sizeof(struct usb_hub_status); + break; + case GetPortStatus: + response_size = sizeof(*response) + + sizeof(struct usb_port_status); + break; + default: + response_size = sizeof(*response) + wLength; + break; + }
operation = gb_operation_create(dev->connection, GB_USB_TYPE_HUB_CONTROL, @@ -126,10 +147,12 @@ static int hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, if (ret) goto out;
- if (wLength) { + if (response_size > sizeof(*response)) { + size_t data_size = response_size - sizeof(*response); + /* Greybus core has verified response size */ response = operation->response->payload; - memcpy(buf, response->buf, wLength); + memcpy(buf, response->buf, data_size); } out: gb_operation_put(operation);