]> git.itanic.dy.fi Git - linux-stable/commitdiff
USB: core: Add routines for endpoint checks in old drivers
authorAlan Stern <stern@rowland.harvard.edu>
Mon, 10 Apr 2023 19:37:07 +0000 (15:37 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 30 May 2023 11:44:10 +0000 (12:44 +0100)
commit 13890626501ffda22b18213ddaf7930473da5792 upstream.

Many of the older USB drivers in the Linux USB stack were written
based simply on a vendor's device specification.  They use the
endpoint information in the spec and assume these endpoints will
always be present, with the properties listed, in any device matching
the given vendor and product IDs.

While that may have been true back then, with spoofing and fuzzing it
is not true any more.  More and more we are finding that those old
drivers need to perform at least a minimum of checking before they try
to use any endpoint other than ep0.

To make this checking as simple as possible, we now add a couple of
utility routines to the USB core.  usb_check_bulk_endpoints() and
usb_check_int_endpoints() take an interface pointer together with a
list of endpoint addresses (numbers and directions).  They check that
the interface's current alternate setting includes endpoints with
those addresses and that each of these endpoints has the right type:
bulk or interrupt, respectively.

Although we already have usb_find_common_endpoints() and related
routines meant for a similar purpose, they are not well suited for
this kind of checking.  Those routines find endpoints of various
kinds, but only one (either the first or the last) of each kind, and
they don't verify that the endpoints' addresses agree with what the
caller expects.

In theory the new routines could be more general: They could take a
particular altsetting as their argument instead of always using the
interface's current altsetting.  In practice I think this won't matter
too much; multiple altsettings tend to be used for transferring media
(audio or visual) over isochronous endpoints, not bulk or interrupt.
Drivers for such devices will generally require more sophisticated
checking than these simplistic routines provide.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Link: https://lore.kernel.org/r/dd2c8e8c-2c87-44ea-ba17-c64b97e201c9@rowland.harvard.edu
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/core/usb.c
include/linux/usb.h

index f16c26dc079d7949fa3c610f2521c832386e3f1e..502d911f71fa658a28f97dc95e024cfd689c93b7 100644 (file)
@@ -208,6 +208,82 @@ int usb_find_common_endpoints_reverse(struct usb_host_interface *alt,
 }
 EXPORT_SYMBOL_GPL(usb_find_common_endpoints_reverse);
 
+/**
+ * usb_find_endpoint() - Given an endpoint address, search for the endpoint's
+ * usb_host_endpoint structure in an interface's current altsetting.
+ * @intf: the interface whose current altsetting should be searched
+ * @ep_addr: the endpoint address (number and direction) to find
+ *
+ * Search the altsetting's list of endpoints for one with the specified address.
+ *
+ * Return: Pointer to the usb_host_endpoint if found, %NULL otherwise.
+ */
+static const struct usb_host_endpoint *usb_find_endpoint(
+               const struct usb_interface *intf, unsigned int ep_addr)
+{
+       int n;
+       const struct usb_host_endpoint *ep;
+
+       n = intf->cur_altsetting->desc.bNumEndpoints;
+       ep = intf->cur_altsetting->endpoint;
+       for (; n > 0; (--n, ++ep)) {
+               if (ep->desc.bEndpointAddress == ep_addr)
+                       return ep;
+       }
+       return NULL;
+}
+
+/**
+ * usb_check_bulk_endpoints - Check whether an interface's current altsetting
+ * contains a set of bulk endpoints with the given addresses.
+ * @intf: the interface whose current altsetting should be searched
+ * @ep_addrs: 0-terminated array of the endpoint addresses (number and
+ * direction) to look for
+ *
+ * Search for endpoints with the specified addresses and check their types.
+ *
+ * Return: %true if all the endpoints are found and are bulk, %false otherwise.
+ */
+bool usb_check_bulk_endpoints(
+               const struct usb_interface *intf, const u8 *ep_addrs)
+{
+       const struct usb_host_endpoint *ep;
+
+       for (; *ep_addrs; ++ep_addrs) {
+               ep = usb_find_endpoint(intf, *ep_addrs);
+               if (!ep || !usb_endpoint_xfer_bulk(&ep->desc))
+                       return false;
+       }
+       return true;
+}
+EXPORT_SYMBOL_GPL(usb_check_bulk_endpoints);
+
+/**
+ * usb_check_int_endpoints - Check whether an interface's current altsetting
+ * contains a set of interrupt endpoints with the given addresses.
+ * @intf: the interface whose current altsetting should be searched
+ * @ep_addrs: 0-terminated array of the endpoint addresses (number and
+ * direction) to look for
+ *
+ * Search for endpoints with the specified addresses and check their types.
+ *
+ * Return: %true if all the endpoints are found and are interrupt,
+ * %false otherwise.
+ */
+bool usb_check_int_endpoints(
+               const struct usb_interface *intf, const u8 *ep_addrs)
+{
+       const struct usb_host_endpoint *ep;
+
+       for (; *ep_addrs; ++ep_addrs) {
+               ep = usb_find_endpoint(intf, *ep_addrs);
+               if (!ep || !usb_endpoint_xfer_int(&ep->desc))
+                       return false;
+       }
+       return true;
+}
+EXPORT_SYMBOL_GPL(usb_check_int_endpoints);
+
 /**
  * usb_find_alt_setting() - Given a configuration, find the alternate setting
  * for the given interface.
index c4e919cbbec7a3a2e843e5e600959ef18e9dcd81..abcf1ce9bb068b71c711f74d7f160b909b64e20e 100644 (file)
@@ -279,6 +279,11 @@ void usb_put_intf(struct usb_interface *intf);
 #define USB_MAXINTERFACES      32
 #define USB_MAXIADS            (USB_MAXINTERFACES/2)
 
+bool usb_check_bulk_endpoints(
+               const struct usb_interface *intf, const u8 *ep_addrs);
+bool usb_check_int_endpoints(
+               const struct usb_interface *intf, const u8 *ep_addrs);
+
 /*
  * USB Resume Timer: Every Host controller driver should drive the resume
  * signalling on the bus for the amount of time defined by this macro.