[PATCH] gpio: mpc8xxx: Fix potential use-after-free of IRQ domain

From: Guangshuo Li

Date: Sat Oct 31 2026 - 09:06:05 EST


GPIO chip cleanup can call the to_irq callback while releasing requested
GPIOs. Removing the IRQ domain first leaves that callback using a freed
domain, both on removal and after an IRQ request failure.

Register domain cleanup before GPIO chip registration so devres removes
the chip before freeing the domain.

This issue was found by our static analysis tool under development.

Fixes: 257e10752c13 ("gpio: mpc8xxx: Use of_mm_gpiochip_remove")
Assisted-by: OpenAI:gpt-6-astra
Signed-off-by: Guangshuo Li <liguangshuo@xxxxxxxxxxxxxxx>
---
Build-tested the changed object with x86_64 allmodconfig (W=1,
CONFIG_WERROR=n).
No runtime or fault-injection testing was performed.

drivers/gpio/gpio-mpc8xxx.c | 22 +++++++++++++++-------
1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c
index a6868f673831..605393d3c992 100644
--- a/drivers/gpio/gpio-mpc8xxx.c
+++ b/drivers/gpio/gpio-mpc8xxx.c
@@ -319,6 +319,14 @@ static const struct of_device_id mpc8xxx_gpio_ids[] = {
{}
};

+static void mpc8xxx_irq_domain_remove(void *data)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = data;
+
+ if (mpc8xxx_gc->irq)
+ irq_domain_remove(mpc8xxx_gc->irq);
+}
+
static int mpc8xxx_probe(struct platform_device *pdev)
{
const struct mpc8xxx_gpio_devtype *devtype = NULL;
@@ -405,6 +413,11 @@ static int mpc8xxx_probe(struct platform_device *pdev)
mpc8xxx_gc->regs + GPIO_DIR);
}

+ /* GPIO chip cleanup still needs the IRQ domain. */
+ ret = devm_add_action_or_reset(dev, mpc8xxx_irq_domain_remove, mpc8xxx_gc);
+ if (ret)
+ return ret;
+
ret = devm_gpiochip_add_data(dev, gc, mpc8xxx_gc);
if (ret) {
dev_err(dev,
@@ -437,7 +450,7 @@ static int mpc8xxx_probe(struct platform_device *pdev)
if (ret) {
dev_err(dev, "failed to devm_request_irq(%d), ret = %d\n",
mpc8xxx_gc->irqn, ret);
- goto err;
+ return ret;
}

ret = devm_device_init_wakeup(dev);
@@ -445,19 +458,14 @@ static int mpc8xxx_probe(struct platform_device *pdev)
return dev_err_probe(dev, ret, "Failed to init wakeup\n");

return 0;
-err:
- irq_domain_remove(mpc8xxx_gc->irq);
- return ret;
}

static void mpc8xxx_remove(struct platform_device *pdev)
{
struct mpc8xxx_gpio_chip *mpc8xxx_gc = platform_get_drvdata(pdev);

- if (mpc8xxx_gc->irq) {
+ if (mpc8xxx_gc->irq)
irq_set_chained_handler_and_data(mpc8xxx_gc->irqn, NULL, NULL);
- irq_domain_remove(mpc8xxx_gc->irq);
- }
}

static int mpc8xxx_suspend(struct device *dev)

base-commit: d89e1d186ec9271bf78e62771779e9fc03bf0fc3