Skip to contentSkip to conversation

Patch "genirq: Validate action before dereferencing it in handle_irq_event_percpu()" has been added to the 4.4-stable tree

PATCH [email protected] 1 participant 1 message Mar 1, 2016

Subsystems (2)
lore
Share & tools
lore

[email protected]

author maintainer Applied
#0 lore raw

This is a note to let you know that I've just added the patch titled

genirq: Validate action before dereferencing it in handle_irq_event_percpu()

to the 4.4-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
genirq-validate-action-before-dereferencing-it-in-handle_irq_event_percpu.patch
and it can be found in the queue-4.4 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.

Quoted 1 line

From 570540d50710ed192e98e2f7f74578c9486b6b05 Mon Sep 17 00:00:00 2001

From: Thomas Gleixner <[email protected]>
Date: Wed, 13 Jan 2016 14:07:25 +0100
Subject: genirq: Validate action before dereferencing it in handle_irq_event_percpu()

From: Thomas Gleixner <[email protected]>

commit 570540d50710ed192e98e2f7f74578c9486b6b05 upstream.

commit 71f64340fc0e changed the handling of irq_desc->action from

CPU 0                   CPU 1
free_irq()              lock(desc)
  lock(desc)            handle_edge_irq()
                        if (desc->action) {
                          handle_irq_event()
                            action = desc->action
                            unlock(desc)
  desc->action = NULL       handle_irq_event_percpu(desc, action)
                              action->xxx

to

CPU 0                   CPU 1
free_irq()              lock(desc)
  lock(desc)            handle_edge_irq()
                        if (desc->action) {
                          handle_irq_event()
                            unlock(desc)
  desc->action = NULL       handle_irq_event_percpu(desc, action)
                              action = desc->action
                              action->xxx

So if free_irq manages to set the action to NULL between the unlock and before the readout, we happily dereference a null pointer.

We could simply revert 71f64340fc0e, but we want to preserve the better code generation. A simple solution is to change the action loop from a do {} while to a while {} loop.

This is safe because we either see a valid desc->action or NULL. If the action is about to be removed it is still valid as free_irq() is blocked on synchronize_irq().

CPU 0                   CPU 1
free_irq()              lock(desc)
  lock(desc)            handle_edge_irq()
                          handle_irq_event(desc)
                            set(INPROGRESS)
                            unlock(desc)
                            handle_irq_event_percpu(desc)
                            action = desc->action
  desc->action = NULL           while (action) {
                                  action->xxx
                                  ...
                                  action = action->next;

sychronize_irq()
while(INPROGRESS); lock(desc)
clr(INPROGRESS)
free(action)

That's basically the same mechanism as we have for shared interrupts. action->next can become NULL while handle_irq_event_percpu() runs. Either it sees the action or NULL. It does not matter, because action itself cannot go away before the interrupt in progress flag has been cleared.

Fixes commit 71f64340fc0e "genirq: Remove the second parameter from handle_irq_event_percpu()"
Reported-by [email protected]
Cc Huang Shijie <[email protected]>
Cc Jiang Liu <[email protected]>
Cc Peter Zijlstra <[email protected]>
---
 kernel/irq/handle.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
kernel/irq/handle.c +3 -2
@@ -138,7 +138,8 @@ irqreturn_t handle_irq_event_percpu(stru
 	unsigned int flags = 0, irq = desc->irq_data.irq;
 	struct irqaction *action = desc->action;
 
-	do {
+	/* action might have become NULL since we dropped the lock */
+	while (action) {
 		irqreturn_t res;
 
 		trace_irq_handler_entry(irq, action);
@@ -173,7 +174,7 @@ irqreturn_t handle_irq_event_percpu(stru
 
 		retval |= res;
 		action = action->next;
-	} while (action);
+	}
 
 	add_interrupt_randomness(irq, flags);
 

Patches currently in stable-queue which might be from [email protected] are

queue-4.4/genirq-validate-action-before-dereferencing-it-in-handle_irq_event_percpu.patch