Skip to content

Commit

Permalink
genirq: prevent wakeup of freed irq thread
Browse files Browse the repository at this point in the history
free_irq() can remove an irqaction while the corresponding interrupt
is in progress, but free_irq() sets action->thread to NULL
unconditionally, which might lead to a NULL pointer dereference in
handle_IRQ_event() when the hard interrupt context tries to wake up
the handler thread.

Prevent this by moving the thread stop after synchronize_irq(). No
need to set action->thread to NULL either as action is going to be
freed anyway.

This fixes a boot crash reported against preempt-rt which uses the
mainline irq threads code to implement full irq threading.

[ tglx: removed local irqthread variable ]

Signed-off-by: Linus Torvalds <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
  • Loading branch information
torvalds authored and KAGA-KOKO committed Aug 13, 2009
1 parent 3493e84 commit 2d860ad
Showing 1 changed file with 7 additions and 10 deletions.
17 changes: 7 additions & 10 deletions kernel/irq/manage.c
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,6 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
{
struct irq_desc *desc = irq_to_desc(irq);
struct irqaction *action, **action_ptr;
struct task_struct *irqthread;
unsigned long flags;

WARN(in_interrupt(), "Trying to free IRQ %d from IRQ context!\n", irq);
Expand Down Expand Up @@ -809,22 +808,13 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
desc->chip->disable(irq);
}

irqthread = action->thread;
action->thread = NULL;

spin_unlock_irqrestore(&desc->lock, flags);

unregister_handler_proc(irq, action);

/* Make sure it's not being used on another CPU: */
synchronize_irq(irq);

if (irqthread) {
if (!test_bit(IRQTF_DIED, &action->thread_flags))
kthread_stop(irqthread);
put_task_struct(irqthread);
}

#ifdef CONFIG_DEBUG_SHIRQ
/*
* It's a shared IRQ -- the driver ought to be prepared for an IRQ
Expand All @@ -840,6 +830,13 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
local_irq_restore(flags);
}
#endif

if (action->thread) {
if (!test_bit(IRQTF_DIED, &action->thread_flags))
kthread_stop(action->thread);
put_task_struct(action->thread);
}

return action;
}

Expand Down

0 comments on commit 2d860ad

Please sign in to comment.