USB: core: Check buffer length matches wLength for control transfers
authorAlan Stern <stern@rowland.harvard.edu>
Wed, 26 May 2021 15:32:44 +0000 (11:32 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 27 May 2021 11:46:21 +0000 (13:46 +0200)
A type of inconsistency that can show up in control URBs is when the
setup packet's wLength value does not match the URB's
transfer_buffer_length field.  The two should always be equal;
differences could lead to information leaks or undefined behavior for
OUT transfers or overruns for IN transfers.

This patch adds a test for such mismatches during URB submission.  If
the test fails, the submission is rejected with a -EBADR error code
(which is not used elsewhere in the USB core), and a debugging message
is logged for people interested in tracking down these errors.

Reviewed-by: Johan Hovold <johan@kernel.org>
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Link: https://lore.kernel.org/r/20210526153244.GA1400430@rowland.harvard.edu
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Documentation/driver-api/usb/error-codes.rst
drivers/usb/core/urb.c

index a3e84bf..8f9790c 100644 (file)
@@ -61,6 +61,9 @@ USB-specific:
                        (c) requested data transfer length is invalid: negative
                            or too large for the host controller.
 
+``-EBADR``             The wLength value in a control URB's setup packet does
+                       not match the URB's transfer_buffer_length.
+
 ``-ENOSPC``            This request would overcommit the usb bandwidth reserved
                        for periodic transfers (interrupt, isochronous).
 
index 279b392..3072772 100644 (file)
@@ -410,6 +410,12 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
                dev_WARN_ONCE(&dev->dev, (usb_pipeout(urb->pipe) != is_out),
                                "BOGUS control dir, pipe %x doesn't match bRequestType %x\n",
                                urb->pipe, setup->bRequestType);
+               if (le16_to_cpu(setup->wLength) != urb->transfer_buffer_length) {
+                       dev_dbg(&dev->dev, "BOGUS control len %d doesn't match transfer length %d\n",
+                                       le16_to_cpu(setup->wLength),
+                                       urb->transfer_buffer_length);
+                       return -EBADR;
+               }
        } else {
                is_out = usb_endpoint_dir_out(&ep->desc);
        }