libpayload: usbmsc: Skip zero-length packets at end of data

Some broken USB mass storage devices send another zero-length packet at
the end of the data part of a transfer if the amount of data was evenly
divisible by the packet size (which is pretty much always the case for
block reads). This packet will get interpreted as the CSW and screw up
the MSC state machine.

This patch works around this issue by retrying the CSW transfer when it
was received as exactly 0 bytes. This is the same mitigation the Linux
kernel uses and harmless for correctly behaving devices. Also tighten
validation of the CSW a little, making sure we verify the length before
we read any fields and checking the signature in addition to the tag.

Change-Id: I24f183f27b2c4f0142ba6c4b35b490c5798d0d21
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/34485
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
Julius Werner 2019-07-22 11:06:40 -07:00 committed by Patrick Georgi
parent c788ae328e
commit 182fea717e
1 changed files with 13 additions and 4 deletions

View File

@ -218,14 +218,23 @@ wrap_cbw (cbw_t *cbw, int datalen, cbw_direction dir, const u8 *cmd,
static int
get_csw (endpoint_t *ep, csw_t *csw)
{
if (ep->dev->controller->bulk (ep, sizeof (csw_t), (u8 *) csw, 1) < 0) {
hci_t *ctrlr = ep->dev->controller;
int ret = ctrlr->bulk (ep, sizeof (csw_t), (u8 *) csw, 1);
/* Some broken sticks send a zero-length packet at the end of their data
transfer which would show up here. Skip it to get the actual CSW. */
if (ret == 0)
ret = ctrlr->bulk (ep, sizeof (csw_t), (u8 *) csw, 1);
if (ret < 0) {
clear_stall (ep);
if (ep->dev->controller->bulk
(ep, sizeof (csw_t), (u8 *) csw, 1) < 0) {
if (ctrlr->bulk (ep, sizeof (csw_t), (u8 *) csw, 1) < 0) {
return reset_transport (ep->dev);
}
}
if (csw->dCSWTag != tag) {
if (ret != sizeof(csw_t) || csw->dCSWTag != tag ||
csw->dCSWSignature != csw_signature) {
usb_debug ("MSC: received malformed CSW\n");
return reset_transport (ep->dev);
}
return MSC_COMMAND_OK;