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
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);
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
© 2016 - 2025 Red Hat, Inc.