[PATCH net v2 3/4] net: dsa: microchip: Ensure a ksz_irq is initialized before freeing it

Bastien Curutchet (Schneider Electric) posted 4 patches 1 month, 1 week ago
There is a newer version of this series
[PATCH net v2 3/4] net: dsa: microchip: Ensure a ksz_irq is initialized before freeing it
Posted by Bastien Curutchet (Schneider Electric) 1 month, 1 week ago
Sometimes ksz_irq_free() can be called on uninitialized ksz_irq (for
example when ksz_ptp_irq_setup() fails). It leads to freeing
uninitialized IRQ numbers and/or domains.

Ensure that IRQ numbers or domains aren't null before freeing them.
In our case the IRQ number of an initialized ksz_irq is never 0. Indeed,
it's either the device's IRQ number and we enter the IRQ setup only when
this dev->irq is strictly positive, or a virtual IRQ assigned with
irq_create_mapping() which returns strictly positive IRQ numbers.

Fixes: e1add7dd6183 ("net: dsa: microchip: use common irq routines for girq and pirq")
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
 drivers/net/dsa/microchip/ksz_common.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 3a4516d32aa5f99109853ed400e64f8f7e2d8016..4f5e2024442692adefc69d47e82381a3c3bda184 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -2858,14 +2858,16 @@ static void ksz_irq_free(struct ksz_irq *kirq)
 {
 	int irq, virq;
 
-	free_irq(kirq->irq_num, kirq);
+	if (kirq->irq_num)
+		free_irq(kirq->irq_num, kirq);
 
 	for (irq = 0; irq < kirq->nirqs; irq++) {
 		virq = irq_find_mapping(kirq->domain, irq);
 		irq_dispose_mapping(virq);
 	}
 
-	irq_domain_remove(kirq->domain);
+	if (kirq->domain)
+		irq_domain_remove(kirq->domain);
 }
 
 static irqreturn_t ksz_irq_thread_fn(int irq, void *dev_id)

-- 
2.51.0
Re: [PATCH net v2 3/4] net: dsa: microchip: Ensure a ksz_irq is initialized before freeing it
Posted by Jakub Kicinski 1 month, 1 week ago
On Thu, 06 Nov 2025 13:53:10 +0100 Bastien Curutchet (Schneider
Electric) wrote:
> diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
> index 3a4516d32aa5f99109853ed400e64f8f7e2d8016..4f5e2024442692adefc69d47e82381a3c3bda184 100644
> --- a/drivers/net/dsa/microchip/ksz_common.c
> +++ b/drivers/net/dsa/microchip/ksz_common.c
> @@ -2858,14 +2858,16 @@ static void ksz_irq_free(struct ksz_irq *kirq)
>  {
>  	int irq, virq;
>  
> -	free_irq(kirq->irq_num, kirq);
> +	if (kirq->irq_num)
> +		free_irq(kirq->irq_num, kirq);
>  
>  	for (irq = 0; irq < kirq->nirqs; irq++) {

if the domain may not be registered is it okay to try to find mappings
in it? From the init path it seems that kirq->nirqs is set to the port
count before registration so it will not be 0 if domain is NULL.

>  		virq = irq_find_mapping(kirq->domain, irq);
>  		irq_dispose_mapping(virq);
>  	}
>  
> -	irq_domain_remove(kirq->domain);
> +	if (kirq->domain)
> +		irq_domain_remove(kirq->domain);
Re: [PATCH net v2 3/4] net: dsa: microchip: Ensure a ksz_irq is initialized before freeing it
Posted by Bastien Curutchet 1 month, 1 week ago
Hi Jakub,

On 11/11/25 3:28 AM, Jakub Kicinski wrote:
> On Thu, 06 Nov 2025 13:53:10 +0100 Bastien Curutchet (Schneider
> Electric) wrote:
>> diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
>> index 3a4516d32aa5f99109853ed400e64f8f7e2d8016..4f5e2024442692adefc69d47e82381a3c3bda184 100644
>> --- a/drivers/net/dsa/microchip/ksz_common.c
>> +++ b/drivers/net/dsa/microchip/ksz_common.c
>> @@ -2858,14 +2858,16 @@ static void ksz_irq_free(struct ksz_irq *kirq)
>>   {
>>   	int irq, virq;
>>   
>> -	free_irq(kirq->irq_num, kirq);
>> +	if (kirq->irq_num)
>> +		free_irq(kirq->irq_num, kirq);
>>   
>>   	for (irq = 0; irq < kirq->nirqs; irq++) {
> 
> if the domain may not be registered is it okay to try to find mappings
> in it? From the init path it seems that kirq->nirqs is set to the port
> count before registration so it will not be 0 if domain is NULL.
> 

I first thought it was fine because __irq_resolve_mapping() inside 
irq_find_mapping() verifies that the domain isn't NULL, then 
irq_find_mapping() returns 0 when it doesn't find an IRQ, and finally,
irq_dispose_mapping() returns immediately when virq is 0.

However, after taking a closer look at __irq_resolve_mapping(), I 
noticed that there is a global irq_default_domain that is used by some 
architectures (mainly MIPS and PowerPC) as a fallback when 
__irq_resolve_mapping() is given a NULL domain. In that case, it seems 
possible for the function to return a non-zero virq that has nothing to 
do with us, and which would then be incorrectly released afterward by 
irq_dispose_mapping().

This second case shouldn't occur often but I can move the for loop 
behind the `if (kirq->domain)` check to be safe.


>>   		virq = irq_find_mapping(kirq->domain, irq);
>>   		irq_dispose_mapping(virq);
>>   	}
>>   
>> -	irq_domain_remove(kirq->domain);
>> +	if (kirq->domain)
>> +		irq_domain_remove(kirq->domain);

Best regards,
Bastien