[PATCH 14/28] mfd: twl-core: Clear twl_priv pointer on teardown and probe failure
From: Lee Jones
Date: Wed Oct 21 2026 - 08:21:24 EST
twl_probe() allocates the file-scope twl_priv state with devm_kzalloc()
and checks for a non-NULL twl_priv at entry to reject multiple device
instances. However, twl_remove() and the early error paths in
twl_probe() never reset twl_priv to NULL, a failure when adding MFD
child devices bypasses twl_remove() by jumping to free instead of fail,
and twl_remove() tears down the IRQ domain and dummy I2C clients before
child devices are unregistered. After devres frees the underlying
memory on probe failure or unbind, twl_priv remains a dangling pointer,
causing subsequent exported API calls to dereference freed memory and
blocking future probe attempts with -EBUSY.
Unregister DT-populated and MFD child devices at the start of
twl_remove(), ensure mfd_add_devices() failures unwind via fail, and
reset twl_priv to NULL in twl_remove() and on probe error exit.
Fixes: 80a97ccd33be ("mfd: twl-core: Collect global variables behind one private structure (global)")
Closes: https://sashiko.dev/#/bug/linux-73e82f84-94de-4d08-82a4-f22d42a1dedf
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Lee Jones <lee@xxxxxxxxxx>
---
drivers/mfd/twl-core.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index 64b45fe79159..21fc0acb827b 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -689,6 +689,11 @@ static void twl_remove(struct i2c_client *client)
{
unsigned i, num_slaves;
+ of_platform_depopulate(&client->dev);
+
+ if (twl_class_is_6030())
+ mfd_remove_devices(&client->dev);
+
if (pm_power_off == twl6030_power_off)
pm_power_off = NULL;
@@ -706,6 +711,7 @@ static void twl_remove(struct i2c_client *client)
twl->client = NULL;
}
twl_priv->ready = false;
+ twl_priv = NULL;
}
@@ -880,10 +886,10 @@ twl_probe(struct i2c_client *client)
num_cells = ARRAY_SIZE(twl6030_cells);
}
- status = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
- cells, num_cells, NULL, 0, NULL);
+ status = mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
+ cells, num_cells, NULL, 0, NULL);
if (status < 0)
- goto free;
+ goto fail;
if (of_device_is_system_power_controller(node)) {
if (!pm_power_off)
@@ -900,8 +906,10 @@ twl_probe(struct i2c_client *client)
if (status < 0)
twl_remove(client);
free:
- if (status < 0)
+ if (status < 0) {
platform_device_unregister(pdev);
+ twl_priv = NULL;
+ }
return status;
}
--
2.56.0.218.g1ec904b173-goog