Re: [PATCH 05/16] irqchip/bcm7038-l1: clean up init failure paths

Florian Fainelli <[email protected]> Tue, 28 Jul 2026 10:36:02 -0700
Newsgroups org.kernel.vger.linux-mips,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
On 7/14/26 06:24, Haofeng Li wrote:
> From: Haofeng Li <[email protected]>
> 
> bcm7038_l1_init_one() stores the allocated CPU object in
> intc->cpus[idx] before mapping its registers and parent interrupt.
> Failures after that allocation leak the CPU object and possibly its
> MMIO mapping.
> 
> A later IRQ domain allocation failure also frees intc while leaving
> parent mappings and chained handlers installed for CPUs initialized
> successfully. Those handlers retain intc as their data and can access
> freed memory.
> 
> Free the current CPU object on local failures. Remember every parent
> IRQ so the common error path can disable wake when it was enabled,
> remove the chained handler, dispose the mapping, unmap the registers,
> and free the CPU object before freeing intc.
> 
> Fixes: 5f7f0317ed28 ("IRQCHIP: Add new driver for BCM7038-style level 1 interrupt controllers")
> 
> Signed-off-by: Haofeng Li <[email protected]>
> ---
>   drivers/irqchip/irq-bcm7038-l1.c | 24 ++++++++++++++++++++----
>   1 file changed, 20 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/irqchip/irq-bcm7038-l1.c b/drivers/irqchip/irq-bcm7038-l1.c
> index 54a8557ef557..b42c4f4df6bf 100644
> --- a/drivers/irqchip/irq-bcm7038-l1.c
> +++ b/drivers/irqchip/irq-bcm7038-l1.c
> @@ -49,6 +49,8 @@ struct bcm7038_l1_chip {
>   
>   struct bcm7038_l1_cpu {
>   	void __iomem		*map_base;
> +	unsigned int		parent_irq;
> +	bool			wake_enabled;
>   	u32			mask_cache[];
>   };
>   
> @@ -247,8 +249,11 @@ static int bcm7038_l1_init_one(struct device_node *dn, unsigned int idx,
>   		return -ENOMEM;
>   
>   	cpu->map_base = ioremap(res.start, sz);
> -	if (!cpu->map_base)
> +	if (!cpu->map_base) {
> +		kfree(cpu);
> +		intc->cpus[idx] = NULL;

This pattern repeats below, consider creating a new label, that would do 
the 'cpu' variable freeing and assigng intc->cpus[idx] to NULL?

>   		return -ENOMEM;
> +	}
>   
>   	for (i = 0; i < n_words; i++) {
>   		l1_writel(~intc->irq_fwd_mask[i],
> @@ -261,12 +266,17 @@ static int bcm7038_l1_init_one(struct device_node *dn, unsigned int idx,
>   	parent_irq = irq_of_parse_and_map(dn, idx);
>   	if (!parent_irq) {
>   		pr_err("failed to map parent interrupt %d\n", parent_irq);
> +		iounmap(cpu->map_base);

And this would belong to a new label as well

> +		kfree(cpu);
> +		intc->cpus[idx] = NULL;

That new label would be used here.

>   		return -EINVAL;
>   	}
>   
> -	if (of_property_read_bool(dn, "brcm,irq-can-wake"))
> -		enable_irq_wake(parent_irq);
> +	if (of_property_read_bool(dn, "brcm,irq-can-wake") &&
> +	    !enable_irq_wake(parent_irq))
> +		cpu->wake_enabled = true;
>   
> +	cpu->parent_irq = parent_irq;
>   	irq_set_chained_handler_and_data(parent_irq, bcm7038_l1_irq_handle,
>   					 intc);
>   
> @@ -408,7 +418,7 @@ static int bcm7038_l1_probe(struct platform_device *pdev, struct device_node *pa
>   			if (idx)
>   				break;
>   			pr_err("failed to remap intc L1 registers\n");
> -			goto out_free;
> +			goto out_unmap;
>   		}
>   	}
>   
> @@ -440,6 +450,12 @@ static int bcm7038_l1_probe(struct platform_device *pdev, struct device_node *pa
>   		struct bcm7038_l1_cpu *cpu = intc->cpus[idx];
>   
>   		if (cpu) {
> +			if (cpu->parent_irq) {
> +				if (cpu->wake_enabled)
> +					disable_irq_wake(cpu->parent_irq);
> +				irq_set_chained_handler_and_data(cpu->parent_irq, NULL, NULL);
> +				irq_dispose_mapping(cpu->parent_irq);
> +			}
>   			if (cpu->map_base)
>   				iounmap(cpu->map_base);
>   			kfree(cpu);

-- 
Florian