Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: typec: class: Fix NULL pointer access

Concurrent calls to typec_partner_unlink_device can lead to a NULL pointer
dereference. This patch adds a mutex to protect USB device pointers and
prevent this issue. The same mutex protects both the device pointers and
the partner device registration.

Cc: stable@vger.kernel.org
Fixes: 59de2a56d127 ("usb: typec: Link enumerated USB devices with Type-C partner")
Signed-off-by: Andrei Kuchynski <akuchynski@chromium.org>
Reviewed-by: Benson Leung <bleung@chromium.org>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://lore.kernel.org/r/20250321143728.4092417-2-akuchynski@chromium.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Andrei Kuchynski and committed by
Greg Kroah-Hartman
ec27386d bea5892d

+14 -2
+13 -2
drivers/usb/typec/class.c
··· 1052 1052 partner->usb_mode = USB_MODE_USB3; 1053 1053 } 1054 1054 1055 + mutex_lock(&port->partner_link_lock); 1055 1056 ret = device_register(&partner->dev); 1056 1057 if (ret) { 1057 1058 dev_err(&port->dev, "failed to register partner (%d)\n", ret); ··· 1064 1063 typec_partner_link_device(partner, port->usb2_dev); 1065 1064 if (port->usb3_dev) 1066 1065 typec_partner_link_device(partner, port->usb3_dev); 1066 + mutex_unlock(&port->partner_link_lock); 1067 1067 1068 1068 return partner; 1069 1069 } ··· 1085 1083 1086 1084 port = to_typec_port(partner->dev.parent); 1087 1085 1086 + mutex_lock(&port->partner_link_lock); 1088 1087 if (port->usb2_dev) 1089 1088 typec_partner_unlink_device(partner, port->usb2_dev); 1090 1089 if (port->usb3_dev) 1091 1090 typec_partner_unlink_device(partner, port->usb3_dev); 1092 1091 1093 1092 device_unregister(&partner->dev); 1093 + mutex_unlock(&port->partner_link_lock); 1094 1094 } 1095 1095 EXPORT_SYMBOL_GPL(typec_unregister_partner); 1096 1096 ··· 2045 2041 static void typec_partner_attach(struct typec_connector *con, struct device *dev) 2046 2042 { 2047 2043 struct typec_port *port = container_of(con, struct typec_port, con); 2048 - struct typec_partner *partner = typec_get_partner(port); 2044 + struct typec_partner *partner; 2049 2045 struct usb_device *udev = to_usb_device(dev); 2050 2046 enum usb_mode usb_mode; 2051 2047 2048 + mutex_lock(&port->partner_link_lock); 2052 2049 if (udev->speed < USB_SPEED_SUPER) { 2053 2050 usb_mode = USB_MODE_USB2; 2054 2051 port->usb2_dev = dev; ··· 2058 2053 port->usb3_dev = dev; 2059 2054 } 2060 2055 2056 + partner = typec_get_partner(port); 2061 2057 if (partner) { 2062 2058 typec_partner_set_usb_mode(partner, usb_mode); 2063 2059 typec_partner_link_device(partner, dev); 2064 2060 put_device(&partner->dev); 2065 2061 } 2062 + mutex_unlock(&port->partner_link_lock); 2066 2063 } 2067 2064 2068 2065 static void typec_partner_deattach(struct typec_connector *con, struct device *dev) 2069 2066 { 2070 2067 struct typec_port *port = container_of(con, struct typec_port, con); 2071 - struct typec_partner *partner = typec_get_partner(port); 2068 + struct typec_partner *partner; 2072 2069 2070 + mutex_lock(&port->partner_link_lock); 2071 + partner = typec_get_partner(port); 2073 2072 if (partner) { 2074 2073 typec_partner_unlink_device(partner, dev); 2075 2074 put_device(&partner->dev); ··· 2083 2074 port->usb2_dev = NULL; 2084 2075 else if (port->usb3_dev == dev) 2085 2076 port->usb3_dev = NULL; 2077 + mutex_unlock(&port->partner_link_lock); 2086 2078 } 2087 2079 2088 2080 /** ··· 2624 2614 2625 2615 ida_init(&port->mode_ids); 2626 2616 mutex_init(&port->port_type_lock); 2617 + mutex_init(&port->partner_link_lock); 2627 2618 2628 2619 port->id = id; 2629 2620 port->ops = cap->ops;
+1
drivers/usb/typec/class.h
··· 59 59 enum typec_port_type port_type; 60 60 enum usb_mode usb_mode; 61 61 struct mutex port_type_lock; 62 + struct mutex partner_link_lock; 62 63 63 64 enum typec_orientation orientation; 64 65 struct typec_switch *sw;