Skip to content

Commit 12ac1d0

Browse files
committed
genirq: Make sparse_irq_lock protect what it should protect
for_each_active_irq() iterates the sparse irq allocation bitmap. The caller must hold sparse_irq_lock. Several code pathes expect that an active bit in the sparse bitmap also has a valid interrupt descriptor. Unfortunately that's not true. The (de)allocation is a two step process, which holds the sparse_irq_lock only across the queue/remove from the radix tree and the set/clear in the allocation bitmap. If a iteration locks sparse_irq_lock between the two steps, then it might see an active bit but the corresponding irq descriptor is NULL. If that is dereferenced unconditionally, then the kernel oopses. Of course, all iterator sites could be audited and fixed, but.... There is no reason why the sparse_irq_lock needs to be dropped between the two steps, in fact the code becomes simpler when the mutex is held across both and the semantics become more straight forward, so future problems of missing NULL pointer checks in the iteration are avoided and all existing sites are fixed in one go. Expand the lock held sections so both operations are covered and the bitmap and the radixtree are in sync. Fixes: a05a900 ("genirq: Make sparse_lock a mutex") Reported-and-tested-by: Huang Ying <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Cc: [email protected]
1 parent 596a7a1 commit 12ac1d0

File tree

1 file changed

+7
-17
lines changed

1 file changed

+7
-17
lines changed

kernel/irq/irqdesc.c

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -421,10 +421,8 @@ static void free_desc(unsigned int irq)
421421
* The sysfs entry must be serialized against a concurrent
422422
* irq_sysfs_init() as well.
423423
*/
424-
mutex_lock(&sparse_irq_lock);
425424
kobject_del(&desc->kobj);
426425
delete_irq_desc(irq);
427-
mutex_unlock(&sparse_irq_lock);
428426

429427
/*
430428
* We free the descriptor, masks and stat fields via RCU. That
@@ -462,20 +460,15 @@ static int alloc_descs(unsigned int start, unsigned int cnt, int node,
462460
desc = alloc_desc(start + i, node, flags, mask, owner);
463461
if (!desc)
464462
goto err;
465-
mutex_lock(&sparse_irq_lock);
466463
irq_insert_desc(start + i, desc);
467464
irq_sysfs_add(start + i, desc);
468-
mutex_unlock(&sparse_irq_lock);
469465
}
466+
bitmap_set(allocated_irqs, start, cnt);
470467
return start;
471468

472469
err:
473470
for (i--; i >= 0; i--)
474471
free_desc(start + i);
475-
476-
mutex_lock(&sparse_irq_lock);
477-
bitmap_clear(allocated_irqs, start, cnt);
478-
mutex_unlock(&sparse_irq_lock);
479472
return -ENOMEM;
480473
}
481474

@@ -575,6 +568,7 @@ static inline int alloc_descs(unsigned int start, unsigned int cnt, int node,
575568

576569
desc->owner = owner;
577570
}
571+
bitmap_set(allocated_irqs, start, cnt);
578572
return start;
579573
}
580574

@@ -670,10 +664,10 @@ void irq_free_descs(unsigned int from, unsigned int cnt)
670664
if (from >= nr_irqs || (from + cnt) > nr_irqs)
671665
return;
672666

667+
mutex_lock(&sparse_irq_lock);
673668
for (i = 0; i < cnt; i++)
674669
free_desc(from + i);
675670

676-
mutex_lock(&sparse_irq_lock);
677671
bitmap_clear(allocated_irqs, from, cnt);
678672
mutex_unlock(&sparse_irq_lock);
679673
}
@@ -720,19 +714,15 @@ __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
720714
from, cnt, 0);
721715
ret = -EEXIST;
722716
if (irq >=0 && start != irq)
723-
goto err;
717+
goto unlock;
724718

725719
if (start + cnt > nr_irqs) {
726720
ret = irq_expand_nr_irqs(start + cnt);
727721
if (ret)
728-
goto err;
722+
goto unlock;
729723
}
730-
731-
bitmap_set(allocated_irqs, start, cnt);
732-
mutex_unlock(&sparse_irq_lock);
733-
return alloc_descs(start, cnt, node, affinity, owner);
734-
735-
err:
724+
ret = alloc_descs(start, cnt, node, affinity, owner);
725+
unlock:
736726
mutex_unlock(&sparse_irq_lock);
737727
return ret;
738728
}

0 commit comments

Comments
 (0)