From: Greg Kroah-Hartman Date: Sun, 10 Sep 2023 11:28:15 +0000 (+0100) Subject: 6.4-stable patches X-Git-Tag: v6.1.53~43 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e863886fff4d60cbdfaacd69554070e3faa1ea39;p=thirdparty%2Fkernel%2Fstable-queue.git 6.4-stable patches added patches: usb-core-change-usb_get_device_descriptor-api.patch usb-core-fix-race-by-not-overwriting-udev-descriptor-in-hub_port_init.patch usb-core-unite-old-scheme-and-new-scheme-descriptor-reads.patch usb-typec-bus-verify-partner-exists-in-typec_altmode_attention.patch usb-typec-tcpm-set-initial-svdm-version-based-on-pd-revision.patch --- diff --git a/queue-6.4/series b/queue-6.4/series index a52a2259435..58c1707f626 100644 --- a/queue-6.4/series +++ b/queue-6.4/series @@ -712,3 +712,8 @@ crypto-stm32-fix-loop-iterating-through-scatterlist-for-dma.patch crypto-stm32-fix-mdmat-condition.patch cpufreq-brcmstb-avs-cpufreq-fix-warray-bounds-bug.patch of-property-fw_devlink-add-a-devlink-for-panel-followers.patch +usb-typec-tcpm-set-initial-svdm-version-based-on-pd-revision.patch +usb-typec-bus-verify-partner-exists-in-typec_altmode_attention.patch +usb-core-unite-old-scheme-and-new-scheme-descriptor-reads.patch +usb-core-change-usb_get_device_descriptor-api.patch +usb-core-fix-race-by-not-overwriting-udev-descriptor-in-hub_port_init.patch diff --git a/queue-6.4/usb-core-change-usb_get_device_descriptor-api.patch b/queue-6.4/usb-core-change-usb_get_device_descriptor-api.patch new file mode 100644 index 00000000000..c638d933b0c --- /dev/null +++ b/queue-6.4/usb-core-change-usb_get_device_descriptor-api.patch @@ -0,0 +1,242 @@ +From de28e469da75359a2bb8cd8778b78aa64b1be1f4 Mon Sep 17 00:00:00 2001 +From: Alan Stern +Date: Fri, 4 Aug 2023 15:12:21 -0400 +Subject: USB: core: Change usb_get_device_descriptor() API + +From: Alan Stern + +commit de28e469da75359a2bb8cd8778b78aa64b1be1f4 upstream. + +The usb_get_device_descriptor() routine reads the device descriptor +from the udev device and stores it directly in udev->descriptor. This +interface is error prone, because the USB subsystem expects in-memory +copies of a device's descriptors to be immutable once the device has +been initialized. + +The interface is changed so that the device descriptor is left in a +kmalloc-ed buffer, not copied into the usb_device structure. A +pointer to the buffer is returned to the caller, who is then +responsible for kfree-ing it. The corresponding changes needed in the +various callers are fairly small. + +Signed-off-by: Alan Stern +Link: https://lore.kernel.org/r/d0111bb6-56c1-4f90-adf2-6cfe152f6561@rowland.harvard.edu +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/core/hcd.c | 10 +++++++--- + drivers/usb/core/hub.c | 44 +++++++++++++++++++++++--------------------- + drivers/usb/core/message.c | 29 ++++++++++++----------------- + drivers/usb/core/usb.h | 4 ++-- + 4 files changed, 44 insertions(+), 43 deletions(-) + +--- a/drivers/usb/core/hcd.c ++++ b/drivers/usb/core/hcd.c +@@ -983,6 +983,7 @@ static int register_root_hub(struct usb_ + { + struct device *parent_dev = hcd->self.controller; + struct usb_device *usb_dev = hcd->self.root_hub; ++ struct usb_device_descriptor *descr; + const int devnum = 1; + int retval; + +@@ -994,13 +995,16 @@ static int register_root_hub(struct usb_ + mutex_lock(&usb_bus_idr_lock); + + usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64); +- retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE); +- if (retval != sizeof usb_dev->descriptor) { ++ descr = usb_get_device_descriptor(usb_dev); ++ if (IS_ERR(descr)) { ++ retval = PTR_ERR(descr); + mutex_unlock(&usb_bus_idr_lock); + dev_dbg (parent_dev, "can't read %s device descriptor %d\n", + dev_name(&usb_dev->dev), retval); +- return (retval < 0) ? retval : -EMSGSIZE; ++ return retval; + } ++ usb_dev->descriptor = *descr; ++ kfree(descr); + + if (le16_to_cpu(usb_dev->descriptor.bcdUSB) >= 0x0201) { + retval = usb_get_bos_descriptor(usb_dev); +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -2656,12 +2656,17 @@ int usb_authorize_device(struct usb_devi + } + + if (usb_dev->wusb) { +- result = usb_get_device_descriptor(usb_dev, sizeof(usb_dev->descriptor)); +- if (result < 0) { ++ struct usb_device_descriptor *descr; ++ ++ descr = usb_get_device_descriptor(usb_dev); ++ if (IS_ERR(descr)) { ++ result = PTR_ERR(descr); + dev_err(&usb_dev->dev, "can't re-read device descriptor for " + "authorization: %d\n", result); + goto error_device_descriptor; + } ++ usb_dev->descriptor = *descr; ++ kfree(descr); + } + + usb_dev->authorized = 1; +@@ -4789,7 +4794,7 @@ hub_port_init(struct usb_hub *hub, struc + const char *driver_name; + bool do_new_scheme; + int maxp0; +- struct usb_device_descriptor *buf; ++ struct usb_device_descriptor *buf, *descr; + + buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO); + if (!buf) +@@ -5031,15 +5036,16 @@ hub_port_init(struct usb_hub *hub, struc + usb_ep0_reinit(udev); + } + +- retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE); +- if (retval < (signed)sizeof(udev->descriptor)) { ++ descr = usb_get_device_descriptor(udev); ++ if (IS_ERR(descr)) { ++ retval = PTR_ERR(descr); + if (retval != -ENODEV) + dev_err(&udev->dev, "device descriptor read/all, error %d\n", + retval); +- if (retval >= 0) +- retval = -ENOMSG; + goto fail; + } ++ udev->descriptor = *descr; ++ kfree(descr); + + /* + * Some superspeed devices have finished the link training process +@@ -5158,7 +5164,7 @@ hub_power_remaining(struct usb_hub *hub) + + + static int descriptors_changed(struct usb_device *udev, +- struct usb_device_descriptor *old_device_descriptor, ++ struct usb_device_descriptor *new_device_descriptor, + struct usb_host_bos *old_bos) + { + int changed = 0; +@@ -5169,8 +5175,8 @@ static int descriptors_changed(struct us + int length; + char *buf; + +- if (memcmp(&udev->descriptor, old_device_descriptor, +- sizeof(*old_device_descriptor)) != 0) ++ if (memcmp(&udev->descriptor, new_device_descriptor, ++ sizeof(*new_device_descriptor)) != 0) + return 1; + + if ((old_bos && !udev->bos) || (!old_bos && udev->bos)) +@@ -5495,9 +5501,8 @@ static void hub_port_connect_change(stru + { + struct usb_port *port_dev = hub->ports[port1 - 1]; + struct usb_device *udev = port_dev->child; +- struct usb_device_descriptor descriptor; ++ struct usb_device_descriptor *descr; + int status = -ENODEV; +- int retval; + + dev_dbg(&port_dev->dev, "status %04x, change %04x, %s\n", portstatus, + portchange, portspeed(hub, portstatus)); +@@ -5524,23 +5529,20 @@ static void hub_port_connect_change(stru + * changed device descriptors before resuscitating the + * device. + */ +- descriptor = udev->descriptor; +- retval = usb_get_device_descriptor(udev, +- sizeof(udev->descriptor)); +- if (retval < 0) { ++ descr = usb_get_device_descriptor(udev); ++ if (IS_ERR(descr)) { + dev_dbg(&udev->dev, +- "can't read device descriptor %d\n", +- retval); ++ "can't read device descriptor %ld\n", ++ PTR_ERR(descr)); + } else { +- if (descriptors_changed(udev, &descriptor, ++ if (descriptors_changed(udev, descr, + udev->bos)) { + dev_dbg(&udev->dev, + "device descriptor has changed\n"); +- /* for disconnect() calls */ +- udev->descriptor = descriptor; + } else { + status = 0; /* Nothing to do */ + } ++ kfree(descr); + } + #ifdef CONFIG_PM + } else if (udev->state == USB_STATE_SUSPENDED && +--- a/drivers/usb/core/message.c ++++ b/drivers/usb/core/message.c +@@ -1040,40 +1040,35 @@ char *usb_cache_string(struct usb_device + EXPORT_SYMBOL_GPL(usb_cache_string); + + /* +- * usb_get_device_descriptor - (re)reads the device descriptor (usbcore) +- * @dev: the device whose device descriptor is being updated +- * @size: how much of the descriptor to read ++ * usb_get_device_descriptor - read the device descriptor ++ * @udev: the device whose device descriptor should be read + * + * Context: task context, might sleep. + * +- * Updates the copy of the device descriptor stored in the device structure, +- * which dedicates space for this purpose. +- * + * Not exported, only for use by the core. If drivers really want to read + * the device descriptor directly, they can call usb_get_descriptor() with + * type = USB_DT_DEVICE and index = 0. + * +- * This call is synchronous, and may not be used in an interrupt context. +- * +- * Return: The number of bytes received on success, or else the status code +- * returned by the underlying usb_control_msg() call. ++ * Returns: a pointer to a dynamically allocated usb_device_descriptor ++ * structure (which the caller must deallocate), or an ERR_PTR value. + */ +-int usb_get_device_descriptor(struct usb_device *dev, unsigned int size) ++struct usb_device_descriptor *usb_get_device_descriptor(struct usb_device *udev) + { + struct usb_device_descriptor *desc; + int ret; + +- if (size > sizeof(*desc)) +- return -EINVAL; + desc = kmalloc(sizeof(*desc), GFP_NOIO); + if (!desc) +- return -ENOMEM; ++ return ERR_PTR(-ENOMEM); ++ ++ ret = usb_get_descriptor(udev, USB_DT_DEVICE, 0, desc, sizeof(*desc)); ++ if (ret == sizeof(*desc)) ++ return desc; + +- ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, size); + if (ret >= 0) +- memcpy(&dev->descriptor, desc, size); ++ ret = -EMSGSIZE; + kfree(desc); +- return ret; ++ return ERR_PTR(ret); + } + + /* +--- a/drivers/usb/core/usb.h ++++ b/drivers/usb/core/usb.h +@@ -43,8 +43,8 @@ extern bool usb_endpoint_is_ignored(stru + struct usb_endpoint_descriptor *epd); + extern int usb_remove_device(struct usb_device *udev); + +-extern int usb_get_device_descriptor(struct usb_device *dev, +- unsigned int size); ++extern struct usb_device_descriptor *usb_get_device_descriptor( ++ struct usb_device *udev); + extern int usb_set_isoch_delay(struct usb_device *dev); + extern int usb_get_bos_descriptor(struct usb_device *dev); + extern void usb_release_bos_descriptor(struct usb_device *dev); diff --git a/queue-6.4/usb-core-fix-race-by-not-overwriting-udev-descriptor-in-hub_port_init.patch b/queue-6.4/usb-core-fix-race-by-not-overwriting-udev-descriptor-in-hub_port_init.patch new file mode 100644 index 00000000000..506b1f2f1f2 --- /dev/null +++ b/queue-6.4/usb-core-fix-race-by-not-overwriting-udev-descriptor-in-hub_port_init.patch @@ -0,0 +1,277 @@ +From ff33299ec8bb80cdcc073ad9c506bd79bb2ed20b Mon Sep 17 00:00:00 2001 +From: Alan Stern +Date: Fri, 4 Aug 2023 15:14:14 -0400 +Subject: USB: core: Fix race by not overwriting udev->descriptor in hub_port_init() + +From: Alan Stern + +commit ff33299ec8bb80cdcc073ad9c506bd79bb2ed20b upstream. + +Syzbot reported an out-of-bounds read in sysfs.c:read_descriptors(): + +BUG: KASAN: slab-out-of-bounds in read_descriptors+0x263/0x280 drivers/usb/core/sysfs.c:883 +Read of size 8 at addr ffff88801e78b8c8 by task udevd/5011 + +CPU: 0 PID: 5011 Comm: udevd Not tainted 6.4.0-rc6-syzkaller-00195-g40f71e7cd3c6 #0 +Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/27/2023 +Call Trace: + + __dump_stack lib/dump_stack.c:88 [inline] + dump_stack_lvl+0xd9/0x150 lib/dump_stack.c:106 + print_address_description.constprop.0+0x2c/0x3c0 mm/kasan/report.c:351 + print_report mm/kasan/report.c:462 [inline] + kasan_report+0x11c/0x130 mm/kasan/report.c:572 + read_descriptors+0x263/0x280 drivers/usb/core/sysfs.c:883 +... +Allocated by task 758: +... + __do_kmalloc_node mm/slab_common.c:966 [inline] + __kmalloc+0x5e/0x190 mm/slab_common.c:979 + kmalloc include/linux/slab.h:563 [inline] + kzalloc include/linux/slab.h:680 [inline] + usb_get_configuration+0x1f7/0x5170 drivers/usb/core/config.c:887 + usb_enumerate_device drivers/usb/core/hub.c:2407 [inline] + usb_new_device+0x12b0/0x19d0 drivers/usb/core/hub.c:2545 + +As analyzed by Khazhy Kumykov, the cause of this bug is a race between +read_descriptors() and hub_port_init(): The first routine uses a field +in udev->descriptor, not expecting it to change, while the second +overwrites it. + +Prior to commit 45bf39f8df7f ("USB: core: Don't hold device lock while +reading the "descriptors" sysfs file") this race couldn't occur, +because the routines were mutually exclusive thanks to the device +locking. Removing that locking from read_descriptors() exposed it to +the race. + +The best way to fix the bug is to keep hub_port_init() from changing +udev->descriptor once udev has been initialized and registered. +Drivers expect the descriptors stored in the kernel to be immutable; +we should not undermine this expectation. In fact, this change should +have been made long ago. + +So now hub_port_init() will take an additional argument, specifying a +buffer in which to store the device descriptor it reads. (If udev has +not yet been initialized, the buffer pointer will be NULL and then +hub_port_init() will store the device descriptor in udev as before.) +This eliminates the data race responsible for the out-of-bounds read. + +The changes to hub_port_init() appear more extensive than they really +are, because of indentation changes resulting from an attempt to avoid +writing to other parts of the usb_device structure after it has been +initialized. Similar changes should be made to the code that reads +the BOS descriptor, but that can be handled in a separate patch later +on. This patch is sufficient to fix the bug found by syzbot. + +Reported-and-tested-by: syzbot+18996170f8096c6174d0@syzkaller.appspotmail.com +Closes: https://lore.kernel.org/linux-usb/000000000000c0ffe505fe86c9ca@google.com/#r +Signed-off-by: Alan Stern +Cc: Khazhy Kumykov +Fixes: 45bf39f8df7f ("USB: core: Don't hold device lock while reading the "descriptors" sysfs file") +Cc: stable@vger.kernel.org +Link: https://lore.kernel.org/r/b958b47a-9a46-4c22-a9f9-e42e42c31251@rowland.harvard.edu +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/core/hub.c | 114 ++++++++++++++++++++++++++++++------------------- + 1 file changed, 70 insertions(+), 44 deletions(-) + +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -4778,10 +4778,17 @@ static int get_bMaxPacketSize0(struct us + * the port lock. For a newly detected device that is not accessible + * through any global pointers, it's not necessary to lock the device, + * but it is still necessary to lock the port. ++ * ++ * For a newly detected device, @dev_descr must be NULL. The device ++ * descriptor retrieved from the device will then be stored in ++ * @udev->descriptor. For an already existing device, @dev_descr ++ * must be non-NULL. The device descriptor will be stored there, ++ * not in @udev->descriptor, because descriptors for registered ++ * devices are meant to be immutable. + */ + static int + hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, +- int retry_counter) ++ int retry_counter, struct usb_device_descriptor *dev_descr) + { + struct usb_device *hdev = hub->hdev; + struct usb_hcd *hcd = bus_to_hcd(hdev->bus); +@@ -4793,6 +4800,7 @@ hub_port_init(struct usb_hub *hub, struc + int devnum = udev->devnum; + const char *driver_name; + bool do_new_scheme; ++ const bool initial = !dev_descr; + int maxp0; + struct usb_device_descriptor *buf, *descr; + +@@ -4831,32 +4839,34 @@ hub_port_init(struct usb_hub *hub, struc + } + oldspeed = udev->speed; + +- /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ... +- * it's fixed size except for full speed devices. +- * For Wireless USB devices, ep0 max packet is always 512 (tho +- * reported as 0xff in the device descriptor). WUSB1.0[4.8.1]. +- */ +- switch (udev->speed) { +- case USB_SPEED_SUPER_PLUS: +- case USB_SPEED_SUPER: +- case USB_SPEED_WIRELESS: /* fixed at 512 */ +- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512); +- break; +- case USB_SPEED_HIGH: /* fixed at 64 */ +- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64); +- break; +- case USB_SPEED_FULL: /* 8, 16, 32, or 64 */ +- /* to determine the ep0 maxpacket size, try to read +- * the device descriptor to get bMaxPacketSize0 and +- * then correct our initial guess. ++ if (initial) { ++ /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ... ++ * it's fixed size except for full speed devices. ++ * For Wireless USB devices, ep0 max packet is always 512 (tho ++ * reported as 0xff in the device descriptor). WUSB1.0[4.8.1]. + */ +- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64); +- break; +- case USB_SPEED_LOW: /* fixed at 8 */ +- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8); +- break; +- default: +- goto fail; ++ switch (udev->speed) { ++ case USB_SPEED_SUPER_PLUS: ++ case USB_SPEED_SUPER: ++ case USB_SPEED_WIRELESS: /* fixed at 512 */ ++ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512); ++ break; ++ case USB_SPEED_HIGH: /* fixed at 64 */ ++ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64); ++ break; ++ case USB_SPEED_FULL: /* 8, 16, 32, or 64 */ ++ /* to determine the ep0 maxpacket size, try to read ++ * the device descriptor to get bMaxPacketSize0 and ++ * then correct our initial guess. ++ */ ++ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64); ++ break; ++ case USB_SPEED_LOW: /* fixed at 8 */ ++ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8); ++ break; ++ default: ++ goto fail; ++ } + } + + if (udev->speed == USB_SPEED_WIRELESS) +@@ -4879,22 +4889,24 @@ hub_port_init(struct usb_hub *hub, struc + if (udev->speed < USB_SPEED_SUPER) + dev_info(&udev->dev, + "%s %s USB device number %d using %s\n", +- (udev->config) ? "reset" : "new", speed, ++ (initial ? "new" : "reset"), speed, + devnum, driver_name); + +- /* Set up TT records, if needed */ +- if (hdev->tt) { +- udev->tt = hdev->tt; +- udev->ttport = hdev->ttport; +- } else if (udev->speed != USB_SPEED_HIGH +- && hdev->speed == USB_SPEED_HIGH) { +- if (!hub->tt.hub) { +- dev_err(&udev->dev, "parent hub has no TT\n"); +- retval = -EINVAL; +- goto fail; ++ if (initial) { ++ /* Set up TT records, if needed */ ++ if (hdev->tt) { ++ udev->tt = hdev->tt; ++ udev->ttport = hdev->ttport; ++ } else if (udev->speed != USB_SPEED_HIGH ++ && hdev->speed == USB_SPEED_HIGH) { ++ if (!hub->tt.hub) { ++ dev_err(&udev->dev, "parent hub has no TT\n"); ++ retval = -EINVAL; ++ goto fail; ++ } ++ udev->tt = &hub->tt; ++ udev->ttport = port1; + } +- udev->tt = &hub->tt; +- udev->ttport = port1; + } + + /* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way? +@@ -4928,6 +4940,12 @@ hub_port_init(struct usb_hub *hub, struc + + maxp0 = get_bMaxPacketSize0(udev, buf, + GET_DESCRIPTOR_BUFSIZE, retries == 0); ++ if (maxp0 > 0 && !initial && ++ maxp0 != udev->descriptor.bMaxPacketSize0) { ++ dev_err(&udev->dev, "device reset changed ep0 maxpacket size!\n"); ++ retval = -ENODEV; ++ goto fail; ++ } + + retval = hub_port_reset(hub, port1, udev, delay, false); + if (retval < 0) /* error or disconnect */ +@@ -5001,6 +5019,12 @@ hub_port_init(struct usb_hub *hub, struc + } else { + u32 delay; + ++ if (!initial && maxp0 != udev->descriptor.bMaxPacketSize0) { ++ dev_err(&udev->dev, "device reset changed ep0 maxpacket size!\n"); ++ retval = -ENODEV; ++ goto fail; ++ } ++ + delay = udev->parent->hub_delay; + udev->hub_delay = min_t(u32, delay, + USB_TP_TRANSMISSION_DELAY_MAX); +@@ -5044,7 +5068,10 @@ hub_port_init(struct usb_hub *hub, struc + retval); + goto fail; + } +- udev->descriptor = *descr; ++ if (initial) ++ udev->descriptor = *descr; ++ else ++ *dev_descr = *descr; + kfree(descr); + + /* +@@ -5354,7 +5381,7 @@ static void hub_port_connect(struct usb_ + } + + /* reset (non-USB 3.0 devices) and get descriptor */ +- status = hub_port_init(hub, udev, port1, i); ++ status = hub_port_init(hub, udev, port1, i, NULL); + if (status < 0) + goto loop; + +@@ -5984,7 +6011,7 @@ static int usb_reset_and_verify_device(s + struct usb_device *parent_hdev = udev->parent; + struct usb_hub *parent_hub; + struct usb_hcd *hcd = bus_to_hcd(udev->bus); +- struct usb_device_descriptor descriptor = udev->descriptor; ++ struct usb_device_descriptor descriptor; + struct usb_host_bos *bos; + int i, j, ret = 0; + int port1 = udev->portnum; +@@ -6020,7 +6047,7 @@ static int usb_reset_and_verify_device(s + /* ep0 maxpacket size may change; let the HCD know about it. + * Other endpoints will be handled by re-enumeration. */ + usb_ep0_reinit(udev); +- ret = hub_port_init(parent_hub, udev, port1, i); ++ ret = hub_port_init(parent_hub, udev, port1, i, &descriptor); + if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV) + break; + } +@@ -6032,7 +6059,6 @@ static int usb_reset_and_verify_device(s + /* Device might have changed firmware (DFU or similar) */ + if (descriptors_changed(udev, &descriptor, bos)) { + dev_info(&udev->dev, "device firmware changed\n"); +- udev->descriptor = descriptor; /* for disconnect() calls */ + goto re_enumerate; + } + diff --git a/queue-6.4/usb-core-unite-old-scheme-and-new-scheme-descriptor-reads.patch b/queue-6.4/usb-core-unite-old-scheme-and-new-scheme-descriptor-reads.patch new file mode 100644 index 00000000000..bccc98802a8 --- /dev/null +++ b/queue-6.4/usb-core-unite-old-scheme-and-new-scheme-descriptor-reads.patch @@ -0,0 +1,328 @@ +From 85d07c55621676d47d873d2749b88f783cd4d5a1 Mon Sep 17 00:00:00 2001 +From: Alan Stern +Date: Fri, 4 Aug 2023 15:10:59 -0400 +Subject: USB: core: Unite old scheme and new scheme descriptor reads + +From: Alan Stern + +commit 85d07c55621676d47d873d2749b88f783cd4d5a1 upstream. + +In preparation for reworking the usb_get_device_descriptor() routine, +it is desirable to unite the two different code paths responsible for +initially determining endpoint 0's maximum packet size in a newly +discovered USB device. Making this determination presents a +chicken-and-egg sort of problem, in that the only way to learn the +maxpacket value is to get it from the device descriptor retrieved from +the device, but communicating with the device to retrieve a descriptor +requires us to know beforehand the ep0 maxpacket size. + +In practice this problem is solved in two different ways, referred to +in hub.c as the "old scheme" and the "new scheme". The old scheme +(which is the approach recommended by the USB-2 spec) involves asking +the device to send just the first eight bytes of its device +descriptor. Such a transfer uses packets containing no more than +eight bytes each, and every USB device must have an ep0 maxpacket size +>= 8, so this should succeed. Since the bMaxPacketSize0 field of the +device descriptor lies within the first eight bytes, this is all we +need. + +The new scheme is an imitation of the technique used in an early +Windows USB implementation, giving it the happy advantage of working +with a wide variety of devices (some of them at the time would not +work with the old scheme, although that's probably less true now). It +involves making an initial guess of the ep0 maxpacket size, asking the +device to send up to 64 bytes worth of its device descriptor (which is +only 18 bytes long), and then resetting the device to clear any error +condition that might have resulted from the guess being wrong. The +initial guess is determined by the connection speed; it should be +correct in all cases other than full speed, for which the allowed +values are 8, 16, 32, and 64 (in this case the initial guess is 64). + +The reason for this patch is that the old- and new-scheme parts of +hub_port_init() use different code paths, one involving +usb_get_device_descriptor() and one not, for their initial reads of +the device descriptor. Since these reads have essentially the same +purpose and are made under essentially the same circumstances, this is +illogical. It makes more sense to have both of them use a common +subroutine. + +This subroutine does basically what the new scheme's code did, because +that approach is more general than the one used by the old scheme. It +only needs to know how many bytes to transfer and whether or not it is +being called for the first iteration of a retry loop (in case of +certain time-out errors). There are two main differences from the +former code: + + We initialize the bDescriptorType field of the transfer buffer + to 0 before performing the transfer, to avoid possibly + accessing an uninitialized value afterward. + + We read the device descriptor into a temporary buffer rather + than storing it directly into udev->descriptor, which the old + scheme implementation used to do. + +Since the whole point of this first read of the device descriptor is +to determine the bMaxPacketSize0 value, that is what the new routine +returns (or an error code). The value is stored in a local variable +rather than in udev->descriptor. As a side effect, this necessitates +moving a section of code that checks the bcdUSB field for SuperSpeed +devices until after the full device descriptor has been retrieved. + +Signed-off-by: Alan Stern +Cc: Oliver Neukum +Link: https://lore.kernel.org/r/495cb5d4-f956-4f4a-a875-1e67e9489510@rowland.harvard.edu +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/core/hub.c | 173 ++++++++++++++++++++++++++----------------------- + 1 file changed, 94 insertions(+), 79 deletions(-) + +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -4703,6 +4703,67 @@ static int hub_enable_device(struct usb_ + return hcd->driver->enable_device(hcd, udev); + } + ++/* ++ * Get the bMaxPacketSize0 value during initialization by reading the ++ * device's device descriptor. Since we don't already know this value, ++ * the transfer is unsafe and it ignores I/O errors, only testing for ++ * reasonable received values. ++ * ++ * For "old scheme" initialization, size will be 8 so we read just the ++ * start of the device descriptor, which should work okay regardless of ++ * the actual bMaxPacketSize0 value. For "new scheme" initialization, ++ * size will be 64 (and buf will point to a sufficiently large buffer), ++ * which might not be kosher according to the USB spec but it's what ++ * Windows does and what many devices expect. ++ * ++ * Returns: bMaxPacketSize0 or a negative error code. ++ */ ++static int get_bMaxPacketSize0(struct usb_device *udev, ++ struct usb_device_descriptor *buf, int size, bool first_time) ++{ ++ int i, rc; ++ ++ /* ++ * Retry on all errors; some devices are flakey. ++ * 255 is for WUSB devices, we actually need to use ++ * 512 (WUSB1.0[4.8.1]). ++ */ ++ for (i = 0; i < GET_MAXPACKET0_TRIES; ++i) { ++ /* Start with invalid values in case the transfer fails */ ++ buf->bDescriptorType = buf->bMaxPacketSize0 = 0; ++ rc = usb_control_msg(udev, usb_rcvaddr0pipe(), ++ USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, ++ USB_DT_DEVICE << 8, 0, ++ buf, size, ++ initial_descriptor_timeout); ++ switch (buf->bMaxPacketSize0) { ++ case 8: case 16: case 32: case 64: case 255: ++ if (buf->bDescriptorType == USB_DT_DEVICE) { ++ rc = buf->bMaxPacketSize0; ++ break; ++ } ++ fallthrough; ++ default: ++ if (rc >= 0) ++ rc = -EPROTO; ++ break; ++ } ++ ++ /* ++ * Some devices time out if they are powered on ++ * when already connected. They need a second ++ * reset, so return early. But only on the first ++ * attempt, lest we get into a time-out/reset loop. ++ */ ++ if (rc > 0 || (rc == -ETIMEDOUT && first_time && ++ udev->speed > USB_SPEED_FULL)) ++ break; ++ } ++ return rc; ++} ++ ++#define GET_DESCRIPTOR_BUFSIZE 64 ++ + /* Reset device, (re)assign address, get device descriptor. + * Device connection must be stable, no more debouncing needed. + * Returns device in USB_STATE_ADDRESS, except on error. +@@ -4727,6 +4788,12 @@ hub_port_init(struct usb_hub *hub, struc + int devnum = udev->devnum; + const char *driver_name; + bool do_new_scheme; ++ int maxp0; ++ struct usb_device_descriptor *buf; ++ ++ buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO); ++ if (!buf) ++ return -ENOMEM; + + /* root hub ports have a slightly longer reset period + * (from USB 2.0 spec, section 7.1.7.5) +@@ -4846,9 +4913,6 @@ hub_port_init(struct usb_hub *hub, struc + } + + if (do_new_scheme) { +- struct usb_device_descriptor *buf; +- int r = 0; +- + retval = hub_enable_device(udev); + if (retval < 0) { + dev_err(&udev->dev, +@@ -4857,52 +4921,8 @@ hub_port_init(struct usb_hub *hub, struc + goto fail; + } + +-#define GET_DESCRIPTOR_BUFSIZE 64 +- buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO); +- if (!buf) { +- retval = -ENOMEM; +- continue; +- } +- +- /* Retry on all errors; some devices are flakey. +- * 255 is for WUSB devices, we actually need to use +- * 512 (WUSB1.0[4.8.1]). +- */ +- for (operations = 0; operations < GET_MAXPACKET0_TRIES; +- ++operations) { +- buf->bMaxPacketSize0 = 0; +- r = usb_control_msg(udev, usb_rcvaddr0pipe(), +- USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, +- USB_DT_DEVICE << 8, 0, +- buf, GET_DESCRIPTOR_BUFSIZE, +- initial_descriptor_timeout); +- switch (buf->bMaxPacketSize0) { +- case 8: case 16: case 32: case 64: case 255: +- if (buf->bDescriptorType == +- USB_DT_DEVICE) { +- r = 0; +- break; +- } +- fallthrough; +- default: +- if (r == 0) +- r = -EPROTO; +- break; +- } +- /* +- * Some devices time out if they are powered on +- * when already connected. They need a second +- * reset. But only on the first attempt, +- * lest we get into a time out/reset loop +- */ +- if (r == 0 || (r == -ETIMEDOUT && +- retries == 0 && +- udev->speed > USB_SPEED_FULL)) +- break; +- } +- udev->descriptor.bMaxPacketSize0 = +- buf->bMaxPacketSize0; +- kfree(buf); ++ maxp0 = get_bMaxPacketSize0(udev, buf, ++ GET_DESCRIPTOR_BUFSIZE, retries == 0); + + retval = hub_port_reset(hub, port1, udev, delay, false); + if (retval < 0) /* error or disconnect */ +@@ -4913,14 +4933,13 @@ hub_port_init(struct usb_hub *hub, struc + retval = -ENODEV; + goto fail; + } +- if (r) { +- if (r != -ENODEV) ++ if (maxp0 < 0) { ++ if (maxp0 != -ENODEV) + dev_err(&udev->dev, "device descriptor read/64, error %d\n", +- r); +- retval = -EMSGSIZE; ++ maxp0); ++ retval = maxp0; + continue; + } +-#undef GET_DESCRIPTOR_BUFSIZE + } + + /* +@@ -4966,19 +4985,17 @@ hub_port_init(struct usb_hub *hub, struc + break; + } + +- retval = usb_get_device_descriptor(udev, 8); +- if (retval < 8) { ++ /* !do_new_scheme || wusb */ ++ maxp0 = get_bMaxPacketSize0(udev, buf, 8, retries == 0); ++ if (maxp0 < 0) { ++ retval = maxp0; + if (retval != -ENODEV) + dev_err(&udev->dev, + "device descriptor read/8, error %d\n", + retval); +- if (retval >= 0) +- retval = -EMSGSIZE; + } else { + u32 delay; + +- retval = 0; +- + delay = udev->parent->hub_delay; + udev->hub_delay = min_t(u32, delay, + USB_TP_TRANSMISSION_DELAY_MAX); +@@ -4995,27 +5012,10 @@ hub_port_init(struct usb_hub *hub, struc + if (retval) + goto fail; + +- /* +- * Some superspeed devices have finished the link training process +- * and attached to a superspeed hub port, but the device descriptor +- * got from those devices show they aren't superspeed devices. Warm +- * reset the port attached by the devices can fix them. +- */ +- if ((udev->speed >= USB_SPEED_SUPER) && +- (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) { +- dev_err(&udev->dev, "got a wrong device descriptor, " +- "warm reset device\n"); +- hub_port_reset(hub, port1, udev, +- HUB_BH_RESET_TIME, true); +- retval = -EINVAL; +- goto fail; +- } +- +- if (udev->descriptor.bMaxPacketSize0 == 0xff || +- udev->speed >= USB_SPEED_SUPER) ++ if (maxp0 == 0xff || udev->speed >= USB_SPEED_SUPER) + i = 512; + else +- i = udev->descriptor.bMaxPacketSize0; ++ i = maxp0; + if (usb_endpoint_maxp(&udev->ep0.desc) != i) { + if (udev->speed == USB_SPEED_LOW || + !(i == 8 || i == 16 || i == 32 || i == 64)) { +@@ -5041,6 +5041,20 @@ hub_port_init(struct usb_hub *hub, struc + goto fail; + } + ++ /* ++ * Some superspeed devices have finished the link training process ++ * and attached to a superspeed hub port, but the device descriptor ++ * got from those devices show they aren't superspeed devices. Warm ++ * reset the port attached by the devices can fix them. ++ */ ++ if ((udev->speed >= USB_SPEED_SUPER) && ++ (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) { ++ dev_err(&udev->dev, "got a wrong device descriptor, warm reset device\n"); ++ hub_port_reset(hub, port1, udev, HUB_BH_RESET_TIME, true); ++ retval = -EINVAL; ++ goto fail; ++ } ++ + usb_detect_quirks(udev); + + if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) { +@@ -5063,6 +5077,7 @@ fail: + hub_port_disable(hub, port1, 0); + update_devnum(udev, devnum); /* for disconnect processing */ + } ++ kfree(buf); + return retval; + } + diff --git a/queue-6.4/usb-typec-bus-verify-partner-exists-in-typec_altmode_attention.patch b/queue-6.4/usb-typec-bus-verify-partner-exists-in-typec_altmode_attention.patch new file mode 100644 index 00000000000..afd5b972ce5 --- /dev/null +++ b/queue-6.4/usb-typec-bus-verify-partner-exists-in-typec_altmode_attention.patch @@ -0,0 +1,83 @@ +From f23643306430f86e2f413ee2b986e0773e79da31 Mon Sep 17 00:00:00 2001 +From: RD Babiera +Date: Mon, 14 Aug 2023 18:05:59 +0000 +Subject: usb: typec: bus: verify partner exists in typec_altmode_attention + +From: RD Babiera + +commit f23643306430f86e2f413ee2b986e0773e79da31 upstream. + +Some usb hubs will negotiate DisplayPort Alt mode with the device +but will then negotiate a data role swap after entering the alt +mode. The data role swap causes the device to unregister all alt +modes, however the usb hub will still send Attention messages +even after failing to reregister the Alt Mode. type_altmode_attention +currently does not verify whether or not a device's altmode partner +exists, which results in a NULL pointer error when dereferencing +the typec_altmode and typec_altmode_ops belonging to the altmode +partner. + +Verify the presence of a device's altmode partner before sending +the Attention message to the Alt Mode driver. + +Fixes: 8a37d87d72f0 ("usb: typec: Bus type for alternate modes") +Cc: stable@vger.kernel.org +Signed-off-by: RD Babiera +Reviewed-by: Heikki Krogerus +Reviewed-by: Guenter Roeck +Link: https://lore.kernel.org/r/20230814180559.923475-1-rdbabiera@google.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/typec/bus.c | 12 ++++++++++-- + drivers/usb/typec/tcpm/tcpm.c | 3 ++- + include/linux/usb/typec_altmode.h | 2 +- + 3 files changed, 13 insertions(+), 4 deletions(-) + +--- a/drivers/usb/typec/bus.c ++++ b/drivers/usb/typec/bus.c +@@ -183,12 +183,20 @@ EXPORT_SYMBOL_GPL(typec_altmode_exit); + * + * Notifies the partner of @adev about Attention command. + */ +-void typec_altmode_attention(struct typec_altmode *adev, u32 vdo) ++int typec_altmode_attention(struct typec_altmode *adev, u32 vdo) + { +- struct typec_altmode *pdev = &to_altmode(adev)->partner->adev; ++ struct altmode *partner = to_altmode(adev)->partner; ++ struct typec_altmode *pdev; ++ ++ if (!partner) ++ return -ENODEV; ++ ++ pdev = &partner->adev; + + if (pdev->ops && pdev->ops->attention) + pdev->ops->attention(pdev, vdo); ++ ++ return 0; + } + EXPORT_SYMBOL_GPL(typec_altmode_attention); + +--- a/drivers/usb/typec/tcpm/tcpm.c ++++ b/drivers/usb/typec/tcpm/tcpm.c +@@ -1877,7 +1877,8 @@ static void tcpm_handle_vdm_request(stru + } + break; + case ADEV_ATTENTION: +- typec_altmode_attention(adev, p[1]); ++ if (typec_altmode_attention(adev, p[1])) ++ tcpm_log(port, "typec_altmode_attention no port partner altmode"); + break; + } + } +--- a/include/linux/usb/typec_altmode.h ++++ b/include/linux/usb/typec_altmode.h +@@ -67,7 +67,7 @@ struct typec_altmode_ops { + + int typec_altmode_enter(struct typec_altmode *altmode, u32 *vdo); + int typec_altmode_exit(struct typec_altmode *altmode); +-void typec_altmode_attention(struct typec_altmode *altmode, u32 vdo); ++int typec_altmode_attention(struct typec_altmode *altmode, u32 vdo); + int typec_altmode_vdm(struct typec_altmode *altmode, + const u32 header, const u32 *vdo, int count); + int typec_altmode_notify(struct typec_altmode *altmode, unsigned long conf, diff --git a/queue-6.4/usb-typec-tcpm-set-initial-svdm-version-based-on-pd-revision.patch b/queue-6.4/usb-typec-tcpm-set-initial-svdm-version-based-on-pd-revision.patch new file mode 100644 index 00000000000..a6ea7227444 --- /dev/null +++ b/queue-6.4/usb-typec-tcpm-set-initial-svdm-version-based-on-pd-revision.patch @@ -0,0 +1,100 @@ +From c97cd0b4b54eb42aed7f6c3c295a2d137f6d2416 Mon Sep 17 00:00:00 2001 +From: RD Babiera +Date: Mon, 31 Jul 2023 16:59:23 +0000 +Subject: usb: typec: tcpm: set initial svdm version based on pd revision + +From: RD Babiera + +commit c97cd0b4b54eb42aed7f6c3c295a2d137f6d2416 upstream. + +When sending Discover Identity messages to a Port Partner that uses Power +Delivery v2 and SVDM v1, we currently send PD v2 messages with SVDM v2.0, +expecting the port partner to respond with its highest supported SVDM +version as stated in Section 6.4.4.2.3 in the Power Delivery v3 +specification. However, sending SVDM v2 to some Power Delivery v2 port +partners results in a NAK whereas sending SVDM v1 does not. + +NAK messages can be handled by the initiator (PD v3 section 6.4.4.2.5.1), +and one solution could be to resend Discover Identity on a lower SVDM +version if possible. But, Section 6.4.4.3 of PD v2 states that "A NAK +response Should be taken as an indication not to retry that particular +Command." + +Instead, we can set the SVDM version to the maximum one supported by the +negotiated PD revision. When operating in PD v2, this obeys Section +6.4.4.2.3, which states the SVDM field "Shall be set to zero to indicate +Version 1.0." In PD v3, the SVDM field "Shall be set to 01b to indicate +Version 2.0." + +Fixes: c34e85fa69b9 ("usb: typec: tcpm: Send DISCOVER_IDENTITY from dedicated work") +Cc: stable@vger.kernel.org +Signed-off-by: RD Babiera +Reviewed-by: Heikki Krogerus +Link: https://lore.kernel.org/r/20230731165926.1815338-1-rdbabiera@google.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/typec/tcpm/tcpm.c | 35 +++++++++++++++++++++++++++++++---- + 1 file changed, 31 insertions(+), 4 deletions(-) + +--- a/drivers/usb/typec/tcpm/tcpm.c ++++ b/drivers/usb/typec/tcpm/tcpm.c +@@ -3935,6 +3935,29 @@ static enum typec_cc_status tcpm_pwr_opm + } + } + ++static void tcpm_set_initial_svdm_version(struct tcpm_port *port) ++{ ++ switch (port->negotiated_rev) { ++ case PD_REV30: ++ break; ++ /* ++ * 6.4.4.2.3 Structured VDM Version ++ * 2.0 states "At this time, there is only one version (1.0) defined. ++ * This field Shall be set to zero to indicate Version 1.0." ++ * 3.0 states "This field Shall be set to 01b to indicate Version 2.0." ++ * To ensure that we follow the Power Delivery revision we are currently ++ * operating on, downgrade the SVDM version to the highest one supported ++ * by the Power Delivery revision. ++ */ ++ case PD_REV20: ++ typec_partner_set_svdm_version(port->partner, SVDM_VER_1_0); ++ break; ++ default: ++ typec_partner_set_svdm_version(port->partner, SVDM_VER_1_0); ++ break; ++ } ++} ++ + static void run_state_machine(struct tcpm_port *port) + { + int ret; +@@ -4172,10 +4195,12 @@ static void run_state_machine(struct tcp + * For now, this driver only supports SOP for DISCOVER_IDENTITY, thus using + * port->explicit_contract to decide whether to send the command. + */ +- if (port->explicit_contract) ++ if (port->explicit_contract) { ++ tcpm_set_initial_svdm_version(port); + mod_send_discover_delayed_work(port, 0); +- else ++ } else { + port->send_discover = false; ++ } + + /* + * 6.3.5 +@@ -4462,10 +4487,12 @@ static void run_state_machine(struct tcp + * For now, this driver only supports SOP for DISCOVER_IDENTITY, thus using + * port->explicit_contract. + */ +- if (port->explicit_contract) ++ if (port->explicit_contract) { ++ tcpm_set_initial_svdm_version(port); + mod_send_discover_delayed_work(port, 0); +- else ++ } else { + port->send_discover = false; ++ } + + power_supply_changed(port->psy); + break;