diff options
| author | Andrei Kuchynski <akuchynski@chromium.org> | 2025-03-21 14:37:26 +0000 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2025-05-02 07:59:30 +0200 |
| commit | de7c24febd21413ea8f49f61b36338b676c02852 (patch) | |
| tree | dc459510c88d524439892fb097c0aee5a1953eba | |
| parent | 543e0f8765e4740de54135b518d258d8b0a74e8d (diff) | |
| download | linux-de7c24febd21413ea8f49f61b36338b676c02852.tar.gz linux-de7c24febd21413ea8f49f61b36338b676c02852.tar.bz2 linux-de7c24febd21413ea8f49f61b36338b676c02852.zip | |
usb: typec: class: Fix NULL pointer access
commit ec27386de23a511008c53aa2f3434ad180a3ca9a upstream.
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>
| -rw-r--r-- | drivers/usb/typec/class.c | 15 | ||||
| -rw-r--r-- | drivers/usb/typec/class.h | 1 |
2 files changed, 14 insertions, 2 deletions
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 58f40156de56..bdccf77260e6 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -932,6 +932,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port, partner->dev.type = &typec_partner_dev_type; dev_set_name(&partner->dev, "%s-partner", dev_name(&port->dev)); + mutex_lock(&port->partner_link_lock); ret = device_register(&partner->dev); if (ret) { dev_err(&port->dev, "failed to register partner (%d)\n", ret); @@ -943,6 +944,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port, typec_partner_link_device(partner, port->usb2_dev); if (port->usb3_dev) typec_partner_link_device(partner, port->usb3_dev); + mutex_unlock(&port->partner_link_lock); return partner; } @@ -963,12 +965,14 @@ void typec_unregister_partner(struct typec_partner *partner) port = to_typec_port(partner->dev.parent); + mutex_lock(&port->partner_link_lock); if (port->usb2_dev) typec_partner_unlink_device(partner, port->usb2_dev); if (port->usb3_dev) typec_partner_unlink_device(partner, port->usb3_dev); device_unregister(&partner->dev); + mutex_unlock(&port->partner_link_lock); } EXPORT_SYMBOL_GPL(typec_unregister_partner); @@ -1862,25 +1866,30 @@ static struct typec_partner *typec_get_partner(struct typec_port *port) static void typec_partner_attach(struct typec_connector *con, struct device *dev) { struct typec_port *port = container_of(con, struct typec_port, con); - struct typec_partner *partner = typec_get_partner(port); + struct typec_partner *partner; struct usb_device *udev = to_usb_device(dev); + mutex_lock(&port->partner_link_lock); if (udev->speed < USB_SPEED_SUPER) port->usb2_dev = dev; else port->usb3_dev = dev; + partner = typec_get_partner(port); if (partner) { typec_partner_link_device(partner, dev); put_device(&partner->dev); } + mutex_unlock(&port->partner_link_lock); } static void typec_partner_deattach(struct typec_connector *con, struct device *dev) { struct typec_port *port = container_of(con, struct typec_port, con); - struct typec_partner *partner = typec_get_partner(port); + struct typec_partner *partner; + mutex_lock(&port->partner_link_lock); + partner = typec_get_partner(port); if (partner) { typec_partner_unlink_device(partner, dev); put_device(&partner->dev); @@ -1890,6 +1899,7 @@ static void typec_partner_deattach(struct typec_connector *con, struct device *d port->usb2_dev = NULL; else if (port->usb3_dev == dev) port->usb3_dev = NULL; + mutex_unlock(&port->partner_link_lock); } /** @@ -2425,6 +2435,7 @@ struct typec_port *typec_register_port(struct device *parent, ida_init(&port->mode_ids); mutex_init(&port->port_type_lock); + mutex_init(&port->partner_link_lock); port->id = id; port->ops = cap->ops; diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h index 7485cdb9dd20..300312a1c152 100644 --- a/drivers/usb/typec/class.h +++ b/drivers/usb/typec/class.h @@ -56,6 +56,7 @@ struct typec_port { enum typec_pwr_opmode pwr_opmode; enum typec_port_type port_type; struct mutex port_type_lock; + struct mutex partner_link_lock; enum typec_orientation orientation; struct typec_switch *sw; |
