Skip to content

Commit 3568aff

Browse files
quic-bjorandeandersson
authored andcommitted
soc: qcom: pmic_glink: Fix race during initialization
As pointed out by Stephen Boyd it is possible that during initialization of the pmic_glink child drivers, the protection-domain notifiers fires, and the associated work is scheduled, before the client registration returns and as a result the local "client" pointer has been initialized. The outcome of this is a NULL pointer dereference as the "client" pointer is blindly dereferenced. Timeline provided by Stephen: CPU0 CPU1 ---- ---- ucsi->client = NULL; devm_pmic_glink_register_client() client->pdr_notify(client->priv, pg->client_state) pmic_glink_ucsi_pdr_notify() schedule_work(&ucsi->register_work) <schedule away> pmic_glink_ucsi_register() ucsi_register() pmic_glink_ucsi_read_version() pmic_glink_ucsi_read() pmic_glink_ucsi_read() pmic_glink_send(ucsi->client) <client is NULL BAD> ucsi->client = client // Too late! This code is identical across the altmode, battery manager and usci child drivers. Resolve this by splitting the allocation of the "client" object and the registration thereof into two operations. This only happens if the protection domain registry is populated at the time of registration, which by the introduction of commit '1ebcde047c54 ("soc: qcom: add pd-mapper implementation")' became much more likely. Reported-by: Amit Pundir <[email protected]> Closes: https://lore.kernel.org/all/CAMi1Hd2_a7TjA7J9ShrAbNOd_CoZ3D87twmO5t+nZxC9sX18tA@mail.gmail.com/ Reported-by: Johan Hovold <[email protected]> Closes: https://lore.kernel.org/all/[email protected]/ Reported-by: Stephen Boyd <[email protected]> Closes: https://lore.kernel.org/all/CAE-0n52JgfCBWiFQyQWPji8cq_rCsviBpW-m72YitgNfdaEhQg@mail.gmail.com/ Fixes: 58ef4ec ("soc: qcom: pmic_glink: Introduce base PMIC GLINK driver") Cc: [email protected] Reviewed-by: Heikki Krogerus <[email protected]> Reviewed-by: Neil Armstrong <[email protected]> Tested-by: Amit Pundir <[email protected]> Reviewed-by: Johan Hovold <[email protected]> Acked-by: Sebastian Reichel <[email protected]> Tested-by: Johan Hovold <[email protected]> Signed-off-by: Bjorn Andersson <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Bjorn Andersson <[email protected]>
1 parent 924fc22 commit 3568aff

File tree

5 files changed

+55
-33
lines changed

5 files changed

+55
-33
lines changed

drivers/power/supply/qcom_battmgr.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,12 +1385,16 @@ static int qcom_battmgr_probe(struct auxiliary_device *adev,
13851385
"failed to register wireless charing power supply\n");
13861386
}
13871387

1388-
battmgr->client = devm_pmic_glink_register_client(dev,
1389-
PMIC_GLINK_OWNER_BATTMGR,
1390-
qcom_battmgr_callback,
1391-
qcom_battmgr_pdr_notify,
1392-
battmgr);
1393-
return PTR_ERR_OR_ZERO(battmgr->client);
1388+
battmgr->client = devm_pmic_glink_client_alloc(dev, PMIC_GLINK_OWNER_BATTMGR,
1389+
qcom_battmgr_callback,
1390+
qcom_battmgr_pdr_notify,
1391+
battmgr);
1392+
if (IS_ERR(battmgr->client))
1393+
return PTR_ERR(battmgr->client);
1394+
1395+
pmic_glink_client_register(battmgr->client);
1396+
1397+
return 0;
13941398
}
13951399

13961400
static const struct auxiliary_device_id qcom_battmgr_id_table[] = {

drivers/soc/qcom/pmic_glink.c

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,14 @@ static void _devm_pmic_glink_release_client(struct device *dev, void *res)
6666
spin_unlock_irqrestore(&pg->client_lock, flags);
6767
}
6868

69-
struct pmic_glink_client *devm_pmic_glink_register_client(struct device *dev,
70-
unsigned int id,
71-
void (*cb)(const void *, size_t, void *),
72-
void (*pdr)(void *, int),
73-
void *priv)
69+
struct pmic_glink_client *devm_pmic_glink_client_alloc(struct device *dev,
70+
unsigned int id,
71+
void (*cb)(const void *, size_t, void *),
72+
void (*pdr)(void *, int),
73+
void *priv)
7474
{
7575
struct pmic_glink_client *client;
7676
struct pmic_glink *pg = dev_get_drvdata(dev->parent);
77-
unsigned long flags;
7877

7978
client = devres_alloc(_devm_pmic_glink_release_client, sizeof(*client), GFP_KERNEL);
8079
if (!client)
@@ -85,6 +84,18 @@ struct pmic_glink_client *devm_pmic_glink_register_client(struct device *dev,
8584
client->cb = cb;
8685
client->pdr_notify = pdr;
8786
client->priv = priv;
87+
INIT_LIST_HEAD(&client->node);
88+
89+
devres_add(dev, client);
90+
91+
return client;
92+
}
93+
EXPORT_SYMBOL_GPL(devm_pmic_glink_client_alloc);
94+
95+
void pmic_glink_client_register(struct pmic_glink_client *client)
96+
{
97+
struct pmic_glink *pg = client->pg;
98+
unsigned long flags;
8899

89100
mutex_lock(&pg->state_lock);
90101
spin_lock_irqsave(&pg->client_lock, flags);
@@ -95,11 +106,8 @@ struct pmic_glink_client *devm_pmic_glink_register_client(struct device *dev,
95106
spin_unlock_irqrestore(&pg->client_lock, flags);
96107
mutex_unlock(&pg->state_lock);
97108

98-
devres_add(dev, client);
99-
100-
return client;
101109
}
102-
EXPORT_SYMBOL_GPL(devm_pmic_glink_register_client);
110+
EXPORT_SYMBOL_GPL(pmic_glink_client_register);
103111

104112
int pmic_glink_send(struct pmic_glink_client *client, void *data, size_t len)
105113
{

drivers/soc/qcom/pmic_glink_altmode.c

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -520,12 +520,17 @@ static int pmic_glink_altmode_probe(struct auxiliary_device *adev,
520520
return ret;
521521
}
522522

523-
altmode->client = devm_pmic_glink_register_client(dev,
524-
altmode->owner_id,
525-
pmic_glink_altmode_callback,
526-
pmic_glink_altmode_pdr_notify,
527-
altmode);
528-
return PTR_ERR_OR_ZERO(altmode->client);
523+
altmode->client = devm_pmic_glink_client_alloc(dev,
524+
altmode->owner_id,
525+
pmic_glink_altmode_callback,
526+
pmic_glink_altmode_pdr_notify,
527+
altmode);
528+
if (IS_ERR(altmode->client))
529+
return PTR_ERR(altmode->client);
530+
531+
pmic_glink_client_register(altmode->client);
532+
533+
return 0;
529534
}
530535

531536
static const struct auxiliary_device_id pmic_glink_altmode_id_table[] = {

drivers/usb/typec/ucsi/ucsi_glink.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -367,12 +367,16 @@ static int pmic_glink_ucsi_probe(struct auxiliary_device *adev,
367367
ucsi->port_orientation[port] = desc;
368368
}
369369

370-
ucsi->client = devm_pmic_glink_register_client(dev,
371-
PMIC_GLINK_OWNER_USBC,
372-
pmic_glink_ucsi_callback,
373-
pmic_glink_ucsi_pdr_notify,
374-
ucsi);
375-
return PTR_ERR_OR_ZERO(ucsi->client);
370+
ucsi->client = devm_pmic_glink_client_alloc(dev, PMIC_GLINK_OWNER_USBC,
371+
pmic_glink_ucsi_callback,
372+
pmic_glink_ucsi_pdr_notify,
373+
ucsi);
374+
if (IS_ERR(ucsi->client))
375+
return PTR_ERR(ucsi->client);
376+
377+
pmic_glink_client_register(ucsi->client);
378+
379+
return 0;
376380
}
377381

378382
static void pmic_glink_ucsi_remove(struct auxiliary_device *adev)

include/linux/soc/qcom/pmic_glink.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ struct pmic_glink_hdr {
2323

2424
int pmic_glink_send(struct pmic_glink_client *client, void *data, size_t len);
2525

26-
struct pmic_glink_client *devm_pmic_glink_register_client(struct device *dev,
27-
unsigned int id,
28-
void (*cb)(const void *, size_t, void *),
29-
void (*pdr)(void *, int),
30-
void *priv);
26+
struct pmic_glink_client *devm_pmic_glink_client_alloc(struct device *dev,
27+
unsigned int id,
28+
void (*cb)(const void *, size_t, void *),
29+
void (*pdr)(void *, int),
30+
void *priv);
31+
void pmic_glink_client_register(struct pmic_glink_client *client);
3132

3233
#endif

0 commit comments

Comments
 (0)