[PATCH] i3c: master: adi: free the IRQ before unregistering the master

Fan Wu posted 1 patch 1 day, 4 hours ago
drivers/i3c/master/adi-i3c-master.c | 4 ++++
1 file changed, 4 insertions(+)
[PATCH] i3c: master: adi: free the IRQ before unregistering the master
Posted by Fan Wu 1 day, 4 hours ago
adi_i3c_master_irq() is requested with devm_request_irq(), so it stays
registered when adi_i3c_master_remove() runs, and the writes masking the
controller IRQ sources do not synchronize with a handler that is already
running on another CPU. i3c_master_unregister() then frees the
struct i3c_dev_desc objects that master->ibi.slots[] still point to, so
the IBI path in adi_i3c_master_handle_ibi() can dereference freed memory.

Fix this by storing the IRQ number in the private structure and calling
devm_free_irq() after the IRQ sources are masked, so a running handler
is finished before the master is torn down.

This issue was found by an in-house static analysis tool.

Fixes: a79ac2cdc91d ("i3c: master: Add driver for Analog Devices I3C Controller IP")
Cc: stable@vger.kernel.org
Co-developed-by: Song Li <songl@zju.edu.cn>
Signed-off-by: Song Li <songl@zju.edu.cn>
Signed-off-by: Fan Wu <fanwu01@zju.edu.cn>
---
 drivers/i3c/master/adi-i3c-master.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/i3c/master/adi-i3c-master.c b/drivers/i3c/master/adi-i3c-master.c
index 6380a38..9b0ee4e 100644
--- a/drivers/i3c/master/adi-i3c-master.c
+++ b/drivers/i3c/master/adi-i3c-master.c
@@ -115,6 +115,7 @@ struct adi_i3c_master {
 		spinlock_t lock; /* Protect transfer */
 	} xferqueue;
 	void __iomem *regs;
+	int irq;
 	struct clk *clk;
 	unsigned long i3c_scl_lim;
 	struct {
@@ -954,6 +955,7 @@ static int adi_i3c_master_probe(struct platform_device *pdev)
 	irq = platform_get_irq(pdev, 0);
 	if (irq < 0)
 		return irq;
+	master->irq = irq;
 
 	version = readl(master->regs + ADI_AXI_REG_VERSION);
 	if (ADI_AXI_PCORE_VER_MAJOR(version) != 1)
@@ -999,6 +1001,8 @@ static void adi_i3c_master_remove(struct platform_device *pdev)
 	writel(0x00, master->regs + REG_IRQ_MASK);
 	writel(0x01, master->regs + REG_ENABLE);
 
+	devm_free_irq(&pdev->dev, master->irq, master);
+
 	i3c_master_unregister(&master->base);
 }
Re: [PATCH] i3c: master: adi: free the IRQ before unregistering the master
Posted by Frank Li 20 hours ago
On Wed, Sep 23, 2026 at 08:53:40AM +0000, Fan Wu wrote:
> adi_i3c_master_irq() is requested with devm_request_irq(), so it stays
> registered when adi_i3c_master_remove() runs, and the writes masking the
> controller IRQ sources do not synchronize with a handler that is already
> running on another CPU. i3c_master_unregister() then frees the
> struct i3c_dev_desc objects that master->ibi.slots[] still point to, so
> the IBI path in adi_i3c_master_handle_ibi() can dereference freed memory.
>
> Fix this by storing the IRQ number in the private structure and calling
> devm_free_irq() after the IRQ sources are masked, so a running handler
> is finished before the master is torn down.
>
> This issue was found by an in-house static analysis tool.
>
> Fixes: a79ac2cdc91d ("i3c: master: Add driver for Analog Devices I3C Controller IP")
> Cc: stable@vger.kernel.org
> Co-developed-by: Song Li <songl@zju.edu.cn>
> Signed-off-by: Song Li <songl@zju.edu.cn>
> Signed-off-by: Fan Wu <fanwu01@zju.edu.cn>
> ---

Reviewed-by: Frank Li <Frank.Li@nxp.com>

>  drivers/i3c/master/adi-i3c-master.c | 4 ++++
>  1 file changed, 4 insertions(+)
>
> diff --git a/drivers/i3c/master/adi-i3c-master.c b/drivers/i3c/master/adi-i3c-master.c
> index 6380a38..9b0ee4e 100644
> --- a/drivers/i3c/master/adi-i3c-master.c
> +++ b/drivers/i3c/master/adi-i3c-master.c
> @@ -115,6 +115,7 @@ struct adi_i3c_master {
>  		spinlock_t lock; /* Protect transfer */
>  	} xferqueue;
>  	void __iomem *regs;
> +	int irq;
>  	struct clk *clk;
>  	unsigned long i3c_scl_lim;
>  	struct {
> @@ -954,6 +955,7 @@ static int adi_i3c_master_probe(struct platform_device *pdev)
>  	irq = platform_get_irq(pdev, 0);
>  	if (irq < 0)
>  		return irq;
> +	master->irq = irq;
>
>  	version = readl(master->regs + ADI_AXI_REG_VERSION);
>  	if (ADI_AXI_PCORE_VER_MAJOR(version) != 1)
> @@ -999,6 +1001,8 @@ static void adi_i3c_master_remove(struct platform_device *pdev)
>  	writel(0x00, master->regs + REG_IRQ_MASK);
>  	writel(0x01, master->regs + REG_ENABLE);
>
> +	devm_free_irq(&pdev->dev, master->irq, master);
> +
>  	i3c_master_unregister(&master->base);
>  }
>
>