]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.14-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 20 Jan 2015 07:38:28 +0000 (15:38 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 20 Jan 2015 07:38:28 +0000 (15:38 +0800)
added patches:
genirq-prevent-proc-race-against-freeing-of-irq-descriptors.patch

queue-3.14/genirq-prevent-proc-race-against-freeing-of-irq-descriptors.patch [new file with mode: 0644]
queue-3.14/series

diff --git a/queue-3.14/genirq-prevent-proc-race-against-freeing-of-irq-descriptors.patch b/queue-3.14/genirq-prevent-proc-race-against-freeing-of-irq-descriptors.patch
new file mode 100644 (file)
index 0000000..23e896a
--- /dev/null
@@ -0,0 +1,229 @@
+From c291ee622165cb2c8d4e7af63fffd499354a23be Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Thu, 11 Dec 2014 23:01:41 +0100
+Subject: genirq: Prevent proc race against freeing of irq descriptors
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+commit c291ee622165cb2c8d4e7af63fffd499354a23be upstream.
+
+Since the rework of the sparse interrupt code to actually free the
+unused interrupt descriptors there exists a race between the /proc
+interfaces to the irq subsystem and the code which frees the interrupt
+descriptor.
+
+CPU0                           CPU1
+                               show_interrupts()
+                                 desc = irq_to_desc(X);
+free_desc(desc)
+  remove_from_radix_tree();
+  kfree(desc);
+                                 raw_spinlock_irq(&desc->lock);
+
+/proc/interrupts is the only interface which can actively corrupt
+kernel memory via the lock access. /proc/stat can only read from freed
+memory. Extremly hard to trigger, but possible.
+
+The interfaces in /proc/irq/N/ are not affected by this because the
+removal of the proc file is serialized in procfs against concurrent
+readers/writers. The removal happens before the descriptor is freed.
+
+For architectures which have CONFIG_SPARSE_IRQ=n this is a non issue
+as the descriptor is never freed. It's merely cleared out with the irq
+descriptor lock held. So any concurrent proc access will either see
+the old correct value or the cleared out ones.
+
+Protect the lookup and access to the irq descriptor in
+show_interrupts() with the sparse_irq_lock.
+
+Provide kstat_irqs_usr() which is protecting the lookup and access
+with sparse_irq_lock and switch /proc/stat to use it.
+
+Document the existing kstat_irqs interfaces so it's clear that the
+caller needs to take care about protection. The users of these
+interfaces are either not affected due to SPARSE_IRQ=n or already
+protected against removal.
+
+Fixes: 1f5a5b87f78f "genirq: Implement a sane sparse_irq allocator"
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/proc/stat.c              |    2 -
+ include/linux/kernel_stat.h |    1 
+ kernel/irq/internals.h      |    8 ++++++
+ kernel/irq/irqdesc.c        |   52 ++++++++++++++++++++++++++++++++++++++++++++
+ kernel/irq/proc.c           |   22 +++++++++++++++++-
+ 5 files changed, 83 insertions(+), 2 deletions(-)
+
+--- a/fs/proc/stat.c
++++ b/fs/proc/stat.c
+@@ -159,7 +159,7 @@ static int show_stat(struct seq_file *p,
+       /* sum again ? it could be updated? */
+       for_each_irq_nr(j)
+-              seq_put_decimal_ull(p, ' ', kstat_irqs(j));
++              seq_put_decimal_ull(p, ' ', kstat_irqs_usr(j));
+       seq_printf(p,
+               "\nctxt %llu\n"
+--- a/include/linux/kernel_stat.h
++++ b/include/linux/kernel_stat.h
+@@ -74,6 +74,7 @@ static inline unsigned int kstat_softirq
+  * Number of interrupts per specific IRQ source, since bootup
+  */
+ extern unsigned int kstat_irqs(unsigned int irq);
++extern unsigned int kstat_irqs_usr(unsigned int irq);
+ /*
+  * Number of interrupts per cpu, since bootup
+--- a/kernel/irq/internals.h
++++ b/kernel/irq/internals.h
+@@ -74,6 +74,14 @@ extern void irq_percpu_disable(struct ir
+ extern void mask_irq(struct irq_desc *desc);
+ extern void unmask_irq(struct irq_desc *desc);
++#ifdef CONFIG_SPARSE_IRQ
++extern void irq_lock_sparse(void);
++extern void irq_unlock_sparse(void);
++#else
++static inline void irq_lock_sparse(void) { }
++static inline void irq_unlock_sparse(void) { }
++#endif
++
+ extern void init_kstat_irqs(struct irq_desc *desc, int node, int nr);
+ irqreturn_t handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action);
+--- a/kernel/irq/irqdesc.c
++++ b/kernel/irq/irqdesc.c
+@@ -131,6 +131,16 @@ static void free_masks(struct irq_desc *
+ static inline void free_masks(struct irq_desc *desc) { }
+ #endif
++void irq_lock_sparse(void)
++{
++      mutex_lock(&sparse_irq_lock);
++}
++
++void irq_unlock_sparse(void)
++{
++      mutex_unlock(&sparse_irq_lock);
++}
++
+ static struct irq_desc *alloc_desc(int irq, int node, struct module *owner)
+ {
+       struct irq_desc *desc;
+@@ -167,6 +177,12 @@ static void free_desc(unsigned int irq)
+       unregister_irq_proc(irq, desc);
++      /*
++       * sparse_irq_lock protects also show_interrupts() and
++       * kstat_irq_usr(). Once we deleted the descriptor from the
++       * sparse tree we can free it. Access in proc will fail to
++       * lookup the descriptor.
++       */
+       mutex_lock(&sparse_irq_lock);
+       delete_irq_desc(irq);
+       mutex_unlock(&sparse_irq_lock);
+@@ -489,6 +505,15 @@ void dynamic_irq_cleanup(unsigned int ir
+       raw_spin_unlock_irqrestore(&desc->lock, flags);
+ }
++/**
++ * kstat_irqs_cpu - Get the statistics for an interrupt on a cpu
++ * @irq:      The interrupt number
++ * @cpu:      The cpu number
++ *
++ * Returns the sum of interrupt counts on @cpu since boot for
++ * @irq. The caller must ensure that the interrupt is not removed
++ * concurrently.
++ */
+ unsigned int kstat_irqs_cpu(unsigned int irq, int cpu)
+ {
+       struct irq_desc *desc = irq_to_desc(irq);
+@@ -497,6 +522,14 @@ unsigned int kstat_irqs_cpu(unsigned int
+                       *per_cpu_ptr(desc->kstat_irqs, cpu) : 0;
+ }
++/**
++ * kstat_irqs - Get the statistics for an interrupt
++ * @irq:      The interrupt number
++ *
++ * Returns the sum of interrupt counts on all cpus since boot for
++ * @irq. The caller must ensure that the interrupt is not removed
++ * concurrently.
++ */
+ unsigned int kstat_irqs(unsigned int irq)
+ {
+       struct irq_desc *desc = irq_to_desc(irq);
+@@ -509,3 +542,22 @@ unsigned int kstat_irqs(unsigned int irq
+               sum += *per_cpu_ptr(desc->kstat_irqs, cpu);
+       return sum;
+ }
++
++/**
++ * kstat_irqs_usr - Get the statistics for an interrupt
++ * @irq:      The interrupt number
++ *
++ * Returns the sum of interrupt counts on all cpus since boot for
++ * @irq. Contrary to kstat_irqs() this can be called from any
++ * preemptible context. It's protected against concurrent removal of
++ * an interrupt descriptor when sparse irqs are enabled.
++ */
++unsigned int kstat_irqs_usr(unsigned int irq)
++{
++      int sum;
++
++      irq_lock_sparse();
++      sum = kstat_irqs(irq);
++      irq_unlock_sparse();
++      return sum;
++}
+--- a/kernel/irq/proc.c
++++ b/kernel/irq/proc.c
+@@ -15,6 +15,23 @@
+ #include "internals.h"
++/*
++ * Access rules:
++ *
++ * procfs protects read/write of /proc/irq/N/ files against a
++ * concurrent free of the interrupt descriptor. remove_proc_entry()
++ * immediately prevents new read/writes to happen and waits for
++ * already running read/write functions to complete.
++ *
++ * We remove the proc entries first and then delete the interrupt
++ * descriptor from the radix tree and free it. So it is guaranteed
++ * that irq_to_desc(N) is valid as long as the read/writes are
++ * permitted by procfs.
++ *
++ * The read from /proc/interrupts is a different problem because there
++ * is no protection. So the lookup and the access to irqdesc
++ * information must be protected by sparse_irq_lock.
++ */
+ static struct proc_dir_entry *root_irq_dir;
+ #ifdef CONFIG_SMP
+@@ -437,9 +454,10 @@ int show_interrupts(struct seq_file *p,
+               seq_putc(p, '\n');
+       }
++      irq_lock_sparse();
+       desc = irq_to_desc(i);
+       if (!desc)
+-              return 0;
++              goto outsparse;
+       raw_spin_lock_irqsave(&desc->lock, flags);
+       for_each_online_cpu(j)
+@@ -479,6 +497,8 @@ int show_interrupts(struct seq_file *p,
+       seq_putc(p, '\n');
+ out:
+       raw_spin_unlock_irqrestore(&desc->lock, flags);
++outsparse:
++      irq_unlock_sparse();
+       return 0;
+ }
+ #endif
index 6e29095891a2778fefb6ce95ca832cd70230cdc2..c18696e1559b7b0f8b82402f4256e3520aa1640f 100644 (file)
@@ -32,3 +32,4 @@ cfg80211-fix-160-mhz-channels-with-80-80-and-160-mhz-drivers.patch
 rtlwifi-rtl8192ce-set-fw_ready-flag.patch
 hp_accel-add-support-for-hp-zbook-15.patch
 tick-powerclamp-remove-tick_nohz_idle-abuse.patch
+genirq-prevent-proc-race-against-freeing-of-irq-descriptors.patch