[SRU][Focal][PATCH 1/6] USB: hub: Clean up use of port initialization schemes and retries

Yuxuan Luo yuxuan.luo at canonical.com
Wed Oct 11 22:54:07 UTC 2023


From: Alan Stern <stern at rowland.harvard.edu>

The SET_CONFIG_TRIES macro in hub.c is badly named; it controls the
number of port-initialization retry attempts rather than the number of
Set-Configuration attempts.  Furthermore, the USE_NEW_SCHEME macro and
use_new_scheme() function are written in a very confusing manner,
making it almost impossible to figure out exactly what they do or
check that they are correct.

This patch renames SET_CONFIG_TRIES to PORT_INIT_TRIES, removes
USE_NEW_SCHEME entirely, and rewrites use_new_scheme() to be much more
transparent, with added comments explaining how it works.  The patch
also pulls the single call site of use_new_scheme() out from the
Get-Descriptor retry loop (where it returns the same value each time)
and renames the local variable used to store the result.

The overall effect is a minor cleanup.  However, there is one
functional change: If the "use_both_schemes" module parameter isn't
set (by default it is set), the existing code does only two retry
iterations.  After this patch it will always perform four, regardless
of the parameter's value.

Signed-off-by: Alan Stern <stern at rowland.harvard.edu>
Link: https://lore.kernel.org/r/20200928152050.GA134701@rowland.harvard.edu
Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
(backported from commit 19502e6911e4ef4a036344eed36274bb18225033)
[yuxuan.luo: its dependent commit, 6ae6dc22d2d1 (“usb: hub: Fix usb
 enumeration issue due to address0 race”), was backported to Focal rather
 than clean cherry pick, resulting in conflicts.
]
CVE-2023-37453
Signed-off-by: Yuxuan Luo <yuxuan.luo at canonical.com>
---
 drivers/usb/core/hub.c | 49 ++++++++++++++++++++++--------------------
 1 file changed, 26 insertions(+), 23 deletions(-)

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 9b389060e1f3c..3462d59ba53cb 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2718,8 +2718,7 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
 #define PORT_RESET_TRIES	5
 #define SET_ADDRESS_TRIES	2
 #define GET_DESCRIPTOR_TRIES	2
-#define SET_CONFIG_TRIES	(2 * (use_both_schemes + 1))
-#define USE_NEW_SCHEME(i, scheme)	((i) / 2 == (int)(scheme))
+#define PORT_INIT_TRIES		4
 
 #define HUB_ROOT_RESET_TIME	60	/* times are in msec */
 #define HUB_SHORT_RESET_TIME	10
@@ -2727,23 +2726,31 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
 #define HUB_LONG_RESET_TIME	200
 #define HUB_RESET_TIMEOUT	800
 
-/*
- * "New scheme" enumeration causes an extra state transition to be
- * exposed to an xhci host and causes USB3 devices to receive control
- * commands in the default state.  This has been seen to cause
- * enumeration failures, so disable this enumeration scheme for USB3
- * devices.
- */
 static bool use_new_scheme(struct usb_device *udev, int retry,
 			   struct usb_port *port_dev)
 {
 	int old_scheme_first_port =
-		port_dev->quirks & USB_PORT_QUIRK_OLD_SCHEME;
+		(port_dev->quirks & USB_PORT_QUIRK_OLD_SCHEME) ||
+		old_scheme_first;
 
+	/*
+	 * "New scheme" enumeration causes an extra state transition to be
+	 * exposed to an xhci host and causes USB3 devices to receive control
+	 * commands in the default state.  This has been seen to cause
+	 * enumeration failures, so disable this enumeration scheme for USB3
+	 * devices.
+	 */
 	if (udev->speed >= USB_SPEED_SUPER)
 		return false;
 
-	return USE_NEW_SCHEME(retry, old_scheme_first_port || old_scheme_first);
+	/*
+	 * If use_both_schemes is set, use the first scheme (whichever
+	 * it is) for the larger half of the retries, then use the other
+	 * scheme.  Otherwise, use the first scheme for all the retries.
+	 */
+	if (use_both_schemes && retry >= (PORT_INIT_TRIES + 1) / 2)
+		return old_scheme_first_port;	/* Second half */
+	return !old_scheme_first_port;		/* First half or all */
 }
 
 /* Is a USB 3.0 port in the Inactive or Compliance Mode state?
@@ -4657,6 +4664,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
 	const char		*speed;
 	int			devnum = udev->devnum;
 	const char		*driver_name;
+	bool			do_new_scheme;
 
 	/* root hub ports have a slightly longer reset period
 	 * (from USB 2.0 spec, section 7.1.7.5)
@@ -4767,14 +4775,13 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
 	 * first 8 bytes of the device descriptor to get the ep0 maxpacket
 	 * value.
 	 */
-	for (retries = 0; retries < GET_DESCRIPTOR_TRIES; (++retries, msleep(100))) {
-		bool did_new_scheme = false;
+	do_new_scheme = use_new_scheme(udev, retry_counter, port_dev);
 
-		if (use_new_scheme(udev, retry_counter, port_dev)) {
+	for (retries = 0; retries < GET_DESCRIPTOR_TRIES; (++retries, msleep(100))) {
+		if (do_new_scheme) {
 			struct usb_device_descriptor *buf;
 			int r = 0;
 
-			did_new_scheme = true;
 			retval = hub_enable_device(udev);
 			if (retval < 0) {
 				dev_err(&udev->dev,
@@ -4883,11 +4890,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
 			 *  - read ep0 maxpacket even for high and low speed,
 			 */
 			msleep(10);
-			/* use_new_scheme() checks the speed which may have
-			 * changed since the initial look so we cache the result
-			 * in did_new_scheme
-			 */
-			if (did_new_scheme)
+			if (do_new_scheme)
 				break;
 		}
 
@@ -5132,7 +5135,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
 
 	status = 0;
 
-	for (i = 0; i < SET_CONFIG_TRIES; i++) {
+	for (i = 0; i < PORT_INIT_TRIES; i++) {
 		usb_lock_port(port_dev);
 		mutex_lock(hcd->address0_mutex);
 		retry_locked = true;
@@ -5276,7 +5279,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
 			break;
 
 		/* When halfway through our retry count, power-cycle the port */
-		if (i == (SET_CONFIG_TRIES / 2) - 1) {
+		if (i == (PORT_INIT_TRIES - 1) / 2) {
 			dev_info(&port_dev->dev, "attempt power cycle\n");
 			hub_port_power_cycle(hub, port1);
 		}
@@ -5876,7 +5879,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
 
 	mutex_lock(hcd->address0_mutex);
 
-	for (i = 0; i < SET_CONFIG_TRIES; ++i) {
+	for (i = 0; i < PORT_INIT_TRIES; ++i) {
 
 		/* ep0 maxpacket size may change; let the HCD know about it.
 		 * Other endpoints will be handled by re-enumeration. */
-- 
2.34.1




More information about the kernel-team mailing list