Skip to content

Commit 0482c34

Browse files
jwrdegoedegregkh
authored andcommitted
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: bdc62f2 ("usb: typec: ucsi: Simplified registration and I/O API") Cc: [email protected] Reviewed-by: Heikki Krogerus <[email protected]> Signed-off-by: Hans de Goede <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent f87fb98 commit 0482c34

File tree

1 file changed

+9
-13
lines changed

1 file changed

+9
-13
lines changed

drivers/usb/typec/ucsi/ucsi.c

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,12 +1125,11 @@ static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con)
11251125
return NULL;
11261126
}
11271127

1128-
static int ucsi_register_port(struct ucsi *ucsi, int index)
1128+
static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
11291129
{
11301130
struct usb_power_delivery_desc desc = { ucsi->cap.pd_version};
11311131
struct usb_power_delivery_capabilities_desc pd_caps;
11321132
struct usb_power_delivery_capabilities *pd_cap;
1133-
struct ucsi_connector *con = &ucsi->connector[index];
11341133
struct typec_capability *cap = &con->typec_cap;
11351134
enum typec_accessory *accessory = cap->accessory;
11361135
enum usb_role u_role = USB_ROLE_NONE;
@@ -1151,7 +1150,6 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
11511150
init_completion(&con->complete);
11521151
mutex_init(&con->lock);
11531152
INIT_LIST_HEAD(&con->partner_tasks);
1154-
con->num = index + 1;
11551153
con->ucsi = ucsi;
11561154

11571155
cap->fwnode = ucsi_find_fwnode(con);
@@ -1328,7 +1326,7 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
13281326
*/
13291327
static int ucsi_init(struct ucsi *ucsi)
13301328
{
1331-
struct ucsi_connector *con;
1329+
struct ucsi_connector *con, *connector;
13321330
u64 command, ntfy;
13331331
int ret;
13341332
int i;
@@ -1359,16 +1357,16 @@ static int ucsi_init(struct ucsi *ucsi)
13591357
}
13601358

13611359
/* Allocate the connectors. Released in ucsi_unregister() */
1362-
ucsi->connector = kcalloc(ucsi->cap.num_connectors + 1,
1363-
sizeof(*ucsi->connector), GFP_KERNEL);
1364-
if (!ucsi->connector) {
1360+
connector = kcalloc(ucsi->cap.num_connectors + 1, sizeof(*connector), GFP_KERNEL);
1361+
if (!connector) {
13651362
ret = -ENOMEM;
13661363
goto err_reset;
13671364
}
13681365

13691366
/* Register all connectors */
13701367
for (i = 0; i < ucsi->cap.num_connectors; i++) {
1371-
ret = ucsi_register_port(ucsi, i);
1368+
connector[i].num = i + 1;
1369+
ret = ucsi_register_port(ucsi, &connector[i]);
13721370
if (ret)
13731371
goto err_unregister;
13741372
}
@@ -1380,11 +1378,12 @@ static int ucsi_init(struct ucsi *ucsi)
13801378
if (ret < 0)
13811379
goto err_unregister;
13821380

1381+
ucsi->connector = connector;
13831382
ucsi->ntfy = ntfy;
13841383
return 0;
13851384

13861385
err_unregister:
1387-
for (con = ucsi->connector; con->port; con++) {
1386+
for (con = connector; con->port; con++) {
13881387
ucsi_unregister_partner(con);
13891388
ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON);
13901389
ucsi_unregister_port_psy(con);
@@ -1400,10 +1399,7 @@ static int ucsi_init(struct ucsi *ucsi)
14001399
typec_unregister_port(con->port);
14011400
con->port = NULL;
14021401
}
1403-
1404-
kfree(ucsi->connector);
1405-
ucsi->connector = NULL;
1406-
1402+
kfree(connector);
14071403
err_reset:
14081404
memset(&ucsi->cap, 0, sizeof(ucsi->cap));
14091405
ucsi_reset_ppm(ucsi);

0 commit comments

Comments
 (0)