The patch below does not apply to the 6.2-stable tree. If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to stable@vger.kernel.org.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.2.y git checkout FETCH_HEAD git cherry-pick -x 0482c34ec6f8557e06cd0f8e2d0e20e8ede6a22c # <resolve conflicts, build, test, etc.> git commit -s git send-email --to 'stable@vger.kernel.org' --in-reply-to '16800048817970@kroah.com' --subject-prefix 'PATCH 6.2.y' HEAD^..
Possible dependencies:
0482c34ec6f8 ("usb: ucsi: Fix ucsi->connector race") f87fb985452a ("usb: ucsi: Fix NULL pointer deref in ucsi_connector_change()") 924fb3ec50f5 ("Merge 6.2-rc7 into usb-next")
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 0482c34ec6f8557e06cd0f8e2d0e20e8ede6a22c Mon Sep 17 00:00:00 2001 From: Hans de Goede hdegoede@redhat.com Date: Wed, 8 Mar 2023 16:42:43 +0100 Subject: [PATCH] usb: ucsi: Fix ucsi->connector race
ucsi_init() which runs from a workqueue sets ucsi->connector and on an error will clear it again.
ucsi->connector gets dereferenced by ucsi_resume(), this checks for ucsi->connector being NULL in case ucsi_init() has not finished yet; or in case ucsi_init() has failed.
ucsi_init() setting ucsi->connector and then clearing it again on an error creates a race where the check in ucsi_resume() may pass, only to have ucsi->connector free-ed underneath it when ucsi_init() hits an error.
Fix this race by making ucsi_init() store the connector array in a local variable and only assign it to ucsi->connector on success.
Fixes: bdc62f2bae8f ("usb: typec: ucsi: Simplified registration and I/O API") Cc: stable@vger.kernel.org Reviewed-by: Heikki Krogerus heikki.krogerus@linux.intel.com Signed-off-by: Hans de Goede hdegoede@redhat.com Link: https://lore.kernel.org/r/20230308154244.722337-3-hdegoede@redhat.com Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 0623861c597b..8d1baf28df55 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1125,12 +1125,11 @@ static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con) return NULL; }
-static int ucsi_register_port(struct ucsi *ucsi, int index) +static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) { struct usb_power_delivery_desc desc = { ucsi->cap.pd_version}; struct usb_power_delivery_capabilities_desc pd_caps; struct usb_power_delivery_capabilities *pd_cap; - struct ucsi_connector *con = &ucsi->connector[index]; struct typec_capability *cap = &con->typec_cap; enum typec_accessory *accessory = cap->accessory; enum usb_role u_role = USB_ROLE_NONE; @@ -1151,7 +1150,6 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) init_completion(&con->complete); mutex_init(&con->lock); INIT_LIST_HEAD(&con->partner_tasks); - con->num = index + 1; con->ucsi = ucsi;
cap->fwnode = ucsi_find_fwnode(con); @@ -1328,7 +1326,7 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) */ static int ucsi_init(struct ucsi *ucsi) { - struct ucsi_connector *con; + struct ucsi_connector *con, *connector; u64 command, ntfy; int ret; int i; @@ -1359,16 +1357,16 @@ static int ucsi_init(struct ucsi *ucsi) }
/* Allocate the connectors. Released in ucsi_unregister() */ - ucsi->connector = kcalloc(ucsi->cap.num_connectors + 1, - sizeof(*ucsi->connector), GFP_KERNEL); - if (!ucsi->connector) { + connector = kcalloc(ucsi->cap.num_connectors + 1, sizeof(*connector), GFP_KERNEL); + if (!connector) { ret = -ENOMEM; goto err_reset; }
/* Register all connectors */ for (i = 0; i < ucsi->cap.num_connectors; i++) { - ret = ucsi_register_port(ucsi, i); + connector[i].num = i + 1; + ret = ucsi_register_port(ucsi, &connector[i]); if (ret) goto err_unregister; } @@ -1380,11 +1378,12 @@ static int ucsi_init(struct ucsi *ucsi) if (ret < 0) goto err_unregister;
+ ucsi->connector = connector; ucsi->ntfy = ntfy; return 0;
err_unregister: - for (con = ucsi->connector; con->port; con++) { + for (con = connector; con->port; con++) { ucsi_unregister_partner(con); ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON); ucsi_unregister_port_psy(con); @@ -1400,10 +1399,7 @@ static int ucsi_init(struct ucsi *ucsi) typec_unregister_port(con->port); con->port = NULL; } - - kfree(ucsi->connector); - ucsi->connector = NULL; - + kfree(connector); err_reset: memset(&ucsi->cap, 0, sizeof(ucsi->cap)); ucsi_reset_ppm(ucsi);
From: Hans de Goede hdegoede@redhat.com
ucsi_init() which runs from a workqueue sets ucsi->connector and on an error will clear it again.
ucsi->connector gets dereferenced by ucsi_resume(), this checks for ucsi->connector being NULL in case ucsi_init() has not finished yet; or in case ucsi_init() has failed.
ucsi_init() setting ucsi->connector and then clearing it again on an error creates a race where the check in ucsi_resume() may pass, only to have ucsi->connector free-ed underneath it when ucsi_init() hits an error.
Fix this race by making ucsi_init() store the connector array in a local variable and only assign it to ucsi->connector on success.
Fixes: bdc62f2bae8f ("usb: typec: ucsi: Simplified registration and I/O API") Cc: stable@vger.kernel.org Reviewed-by: Heikki Krogerus heikki.krogerus@linux.intel.com Signed-off-by: Hans de Goede hdegoede@redhat.com Link: https://lore.kernel.org/r/20230308154244.722337-3-hdegoede@redhat.com Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org (cherry picked from commit 0482c34ec6f8557e06cd0f8e2d0e20e8ede6a22c) Signed-off-by: Joakim Tjernlund joakim.tjernlund@infinera.com ---
- This is a dry port to 6.1.x, will be some time before it will be tested.
drivers/usb/typec/ucsi/ucsi.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-)
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 8cbbb002fefe..086b50968983 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1039,9 +1039,8 @@ static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con) return NULL; }
-static int ucsi_register_port(struct ucsi *ucsi, int index) +static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) { - struct ucsi_connector *con = &ucsi->connector[index]; struct typec_capability *cap = &con->typec_cap; enum typec_accessory *accessory = cap->accessory; enum usb_role u_role = USB_ROLE_NONE; @@ -1062,7 +1061,6 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) init_completion(&con->complete); mutex_init(&con->lock); INIT_LIST_HEAD(&con->partner_tasks); - con->num = index + 1; con->ucsi = ucsi;
cap->fwnode = ucsi_find_fwnode(con); @@ -1204,7 +1202,7 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) */ static int ucsi_init(struct ucsi *ucsi) { - struct ucsi_connector *con; + struct ucsi_connector *con, *connector; u64 command, ntfy; int ret; int i; @@ -1235,16 +1233,16 @@ static int ucsi_init(struct ucsi *ucsi) }
/* Allocate the connectors. Released in ucsi_unregister() */ - ucsi->connector = kcalloc(ucsi->cap.num_connectors + 1, - sizeof(*ucsi->connector), GFP_KERNEL); - if (!ucsi->connector) { + connector = kcalloc(ucsi->cap.num_connectors + 1, sizeof(*connector), GFP_KERNEL); + if (!connector) { ret = -ENOMEM; goto err_reset; }
/* Register all connectors */ for (i = 0; i < ucsi->cap.num_connectors; i++) { - ret = ucsi_register_port(ucsi, i); + connector[i].num = i + 1; + ret = ucsi_register_port(ucsi, &connector[i]); if (ret) goto err_unregister; } @@ -1256,11 +1254,12 @@ static int ucsi_init(struct ucsi *ucsi) if (ret < 0) goto err_unregister;
+ ucsi->connector = connector; ucsi->ntfy = ntfy; return 0;
err_unregister: - for (con = ucsi->connector; con->port; con++) { + for (con = connector; con->port; con++) { ucsi_unregister_partner(con); ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON); ucsi_unregister_port_psy(con); @@ -1269,10 +1268,7 @@ static int ucsi_init(struct ucsi *ucsi) typec_unregister_port(con->port); con->port = NULL; } - - kfree(ucsi->connector); - ucsi->connector = NULL; - + kfree(connector); err_reset: memset(&ucsi->cap, 0, sizeof(ucsi->cap)); ucsi_reset_ppm(ucsi);
On Wed, 2023-03-29 at 10:03 +0200, Joakim Tjernlund wrote:
From: Hans de Goede hdegoede@redhat.com
ucsi_init() which runs from a workqueue sets ucsi->connector and on an error will clear it again.
ucsi->connector gets dereferenced by ucsi_resume(), this checks for ucsi->connector being NULL in case ucsi_init() has not finished yet; or in case ucsi_init() has failed.
ucsi_init() setting ucsi->connector and then clearing it again on an error creates a race where the check in ucsi_resume() may pass, only to have ucsi->connector free-ed underneath it when ucsi_init() hits an error.
Fix this race by making ucsi_init() store the connector array in a local variable and only assign it to ucsi->connector on success.
Fixes: bdc62f2bae8f ("usb: typec: ucsi: Simplified registration and I/O API") Cc: stable@vger.kernel.org Reviewed-by: Heikki Krogerus heikki.krogerus@linux.intel.com Signed-off-by: Hans de Goede hdegoede@redhat.com Link: https://lore.kernel.org/r/20230308154244.722337-3-hdegoede@redhat.com Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org (cherry picked from commit 0482c34ec6f8557e06cd0f8e2d0e20e8ede6a22c) Signed-off-by: Joakim Tjernlund joakim.tjernlund@infinera.com
- This is a dry port to 6.1.x, will be some time before it will be tested.
Tested OK now on 6.1.22 Jocke
drivers/usb/typec/ucsi/ucsi.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-)
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 8cbbb002fefe..086b50968983 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1039,9 +1039,8 @@ static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con) return NULL; } -static int ucsi_register_port(struct ucsi *ucsi, int index) +static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) {
- struct ucsi_connector *con = &ucsi->connector[index]; struct typec_capability *cap = &con->typec_cap; enum typec_accessory *accessory = cap->accessory; enum usb_role u_role = USB_ROLE_NONE;
@@ -1062,7 +1061,6 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) init_completion(&con->complete); mutex_init(&con->lock); INIT_LIST_HEAD(&con->partner_tasks);
- con->num = index + 1; con->ucsi = ucsi;
cap->fwnode = ucsi_find_fwnode(con); @@ -1204,7 +1202,7 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) */ static int ucsi_init(struct ucsi *ucsi) {
- struct ucsi_connector *con;
- struct ucsi_connector *con, *connector; u64 command, ntfy; int ret; int i;
@@ -1235,16 +1233,16 @@ static int ucsi_init(struct ucsi *ucsi) } /* Allocate the connectors. Released in ucsi_unregister() */
- ucsi->connector = kcalloc(ucsi->cap.num_connectors + 1,
sizeof(*ucsi->connector), GFP_KERNEL);
- if (!ucsi->connector) {
- connector = kcalloc(ucsi->cap.num_connectors + 1, sizeof(*connector), GFP_KERNEL);
- if (!connector) { ret = -ENOMEM; goto err_reset; }
/* Register all connectors */ for (i = 0; i < ucsi->cap.num_connectors; i++) {
ret = ucsi_register_port(ucsi, i);
connector[i].num = i + 1;
if (ret) goto err_unregister; }ret = ucsi_register_port(ucsi, &connector[i]);
@@ -1256,11 +1254,12 @@ static int ucsi_init(struct ucsi *ucsi) if (ret < 0) goto err_unregister;
- ucsi->connector = connector; ucsi->ntfy = ntfy; return 0;
err_unregister:
- for (con = ucsi->connector; con->port; con++) {
- for (con = connector; con->port; con++) { ucsi_unregister_partner(con); ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON); ucsi_unregister_port_psy(con);
@@ -1269,10 +1268,7 @@ static int ucsi_init(struct ucsi *ucsi) typec_unregister_port(con->port); con->port = NULL; }
- kfree(ucsi->connector);
- ucsi->connector = NULL;
- kfree(connector);
err_reset: memset(&ucsi->cap, 0, sizeof(ucsi->cap)); ucsi_reset_ppm(ucsi);
On Thu, Mar 30, 2023 at 04:28:56PM +0000, Joakim Tjernlund wrote:
On Wed, 2023-03-29 at 10:03 +0200, Joakim Tjernlund wrote:
From: Hans de Goede hdegoede@redhat.com
ucsi_init() which runs from a workqueue sets ucsi->connector and on an error will clear it again.
ucsi->connector gets dereferenced by ucsi_resume(), this checks for ucsi->connector being NULL in case ucsi_init() has not finished yet; or in case ucsi_init() has failed.
ucsi_init() setting ucsi->connector and then clearing it again on an error creates a race where the check in ucsi_resume() may pass, only to have ucsi->connector free-ed underneath it when ucsi_init() hits an error.
Fix this race by making ucsi_init() store the connector array in a local variable and only assign it to ucsi->connector on success.
Fixes: bdc62f2bae8f ("usb: typec: ucsi: Simplified registration and I/O API") Cc: stable@vger.kernel.org Reviewed-by: Heikki Krogerus heikki.krogerus@linux.intel.com Signed-off-by: Hans de Goede hdegoede@redhat.com Link: https://lore.kernel.org/r/20230308154244.722337-3-hdegoede@redhat.com Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org (cherry picked from commit 0482c34ec6f8557e06cd0f8e2d0e20e8ede6a22c) Signed-off-by: Joakim Tjernlund joakim.tjernlund@infinera.com
- This is a dry port to 6.1.x, will be some time before it will be tested.
Tested OK now on 6.1.22
Thanks, now queued up for 6.2.y and 6.1.y. Still need backports for older kernels if you want to do that...
thanks,
greg k-h
linux-stable-mirror@lists.linaro.org