[PATCH v2 3/4] mfd: tps65217: Fix irq_domain leak and use-after-free on probe failure

From: Жамбакиев Радий Рикардинович

Date: Fri Aug 21 2026 - 08:39:26 EST


If tps65217_probe() fails after the irq_domain has been created, the
domain is never removed. The tps65217 structure is freed by devres,
leaving the globally registered irq_domain with its host_data pointing
to freed memory, which would trigger a use-after-free if the domain is
ever looked up again, and leaks the domain otherwise.

Move the chip revision read ahead of the IRQ initialization so that
child devices are only probed once the chip has been validated, and
add a cleanup helper that disposes the IRQ mappings and removes the
irq_domain. Call it from the devm_request_threaded_irq() error path in
tps65217_irq_init() and from the devm_mfd_add_devices() error path in
tps65217_probe().

Fixes: 6556bdacf646fcaa ("mfd: tps65217: Add support for IRQs")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Radiy Zhambakiev <r.zhambakiev@xxxxxxxxxxxxxxxxx>
---
drivers/mfd/tps65217.c | 34 +++++++++++++++++++++++++++-------
1 file changed, 27 insertions(+), 7 deletions(-)

diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c
index 9a1528456ffc..d535d140c2e9 100644
--- a/drivers/mfd/tps65217.c
+++ b/drivers/mfd/tps65217.c
@@ -146,6 +146,24 @@ static const struct irq_domain_ops tps65217_irq_domain_ops = {
.map = tps65217_irq_map,
};

+static void tps65217_irq_cleanup(struct tps65217 *tps)
+{
+ unsigned int virq;
+ int i;
+
+ if (!tps->irq_domain)
+ return;
+
+ for (i = 0; i < TPS65217_NUM_IRQ; i++) {
+ virq = irq_find_mapping(tps->irq_domain, i);
+ if (virq)
+ irq_dispose_mapping(virq);
+ }
+
+ irq_domain_remove(tps->irq_domain);
+ tps->irq_domain = NULL;
+}
+
static int tps65217_irq_init(struct tps65217 *tps, int irq)
{
int ret;
@@ -176,6 +194,7 @@ static int tps65217_irq_init(struct tps65217 *tps, int irq)
if (ret) {
dev_err(tps->dev, "Failed to request IRQ %d: %d\n",
irq, ret);
+ tps65217_irq_cleanup(tps);
return ret;
}

@@ -337,6 +356,13 @@ static int tps65217_probe(struct i2c_client *client)
return ret;
}

+ ret = tps65217_reg_read(tps, TPS65217_REG_CHIPID, &version);
+ if (ret < 0) {
+ dev_err(tps->dev, "Failed to read revision register: %d\n",
+ ret);
+ return ret;
+ }
+
if (client->irq) {
ret = tps65217_irq_init(tps, client->irq);
if (ret)
@@ -354,13 +380,7 @@ static int tps65217_probe(struct i2c_client *client)
tps->irq_domain);
if (ret < 0) {
dev_err(tps->dev, "mfd_add_devices failed: %d\n", ret);
- return ret;
- }
-
- ret = tps65217_reg_read(tps, TPS65217_REG_CHIPID, &version);
- if (ret < 0) {
- dev_err(tps->dev, "Failed to read revision register: %d\n",
- ret);
+ tps65217_irq_cleanup(tps);
return ret;
}

--
2.53.0