diff options
author | Jiang Liu <jiang.liu@linux.intel.com> | 2015-12-31 11:30:44 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2016-01-15 07:43:58 -0500 |
commit | 111abeba67e0dbdc26537429de9155e4f1d807d8 (patch) | |
tree | e286d31b727f6f13d12ea3c7625f8adde1e06fbb | |
parent | e23b257c293ce4bcc8cabb2aa3097b6ed8a8261a (diff) |
x86/irq: Fix a race in x86_vector_free_irqs()
There's a race condition between
x86_vector_free_irqs()
{
free_apic_chip_data(irq_data->chip_data);
xxxxx //irq_data->chip_data has been freed, but the pointer
//hasn't been reset yet
irq_domain_reset_irq_data(irq_data);
}
and
smp_irq_move_cleanup_interrupt()
{
raw_spin_lock(&vector_lock);
data = apic_chip_data(irq_desc_get_irq_data(desc));
access data->xxxx // may access freed memory
raw_spin_unlock(&desc->lock);
}
which may cause smp_irq_move_cleanup_interrupt() to access freed memory.
Call irq_domain_reset_irq_data(), which clears the pointer with vector lock
held.
[ tglx: Free memory outside of lock held region. ]
Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Tested-by: Borislav Petkov <bp@alien8.de>
Tested-by: Joe Lawrence <joe.lawrence@stratus.com>
Cc: Jeremiah Mahler <jmmahler@gmail.com>
Cc: andy.shevchenko@gmail.com
Cc: Guenter Roeck <linux@roeck-us.net>
Cc: stable@vger.kernel.org #4.3+
Link: http://lkml.kernel.org/r/1450880014-11741-3-git-send-email-jiang.liu@linux.intel.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | arch/x86/kernel/apic/vector.c | 16 |
1 files changed, 8 insertions, 8 deletions
diff --git a/arch/x86/kernel/apic/vector.c b/arch/x86/kernel/apic/vector.c index 908cb37da171..cf1e325b67ee 100644 --- a/arch/x86/kernel/apic/vector.c +++ b/arch/x86/kernel/apic/vector.c | |||
@@ -226,10 +226,8 @@ static int assign_irq_vector_policy(int irq, int node, | |||
226 | static void clear_irq_vector(int irq, struct apic_chip_data *data) | 226 | static void clear_irq_vector(int irq, struct apic_chip_data *data) |
227 | { | 227 | { |
228 | struct irq_desc *desc; | 228 | struct irq_desc *desc; |
229 | unsigned long flags; | ||
230 | int cpu, vector; | 229 | int cpu, vector; |
231 | 230 | ||
232 | raw_spin_lock_irqsave(&vector_lock, flags); | ||
233 | BUG_ON(!data->cfg.vector); | 231 | BUG_ON(!data->cfg.vector); |
234 | 232 | ||
235 | vector = data->cfg.vector; | 233 | vector = data->cfg.vector; |
@@ -239,10 +237,8 @@ static void clear_irq_vector(int irq, struct apic_chip_data *data) | |||
239 | data->cfg.vector = 0; | 237 | data->cfg.vector = 0; |
240 | cpumask_clear(data->domain); | 238 | cpumask_clear(data->domain); |
241 | 239 | ||
242 | if (likely(!data->move_in_progress)) { | 240 | if (likely(!data->move_in_progress)) |
243 | raw_spin_unlock_irqrestore(&vector_lock, flags); | ||
244 | return; | 241 | return; |
245 | } | ||
246 | 242 | ||
247 | desc = irq_to_desc(irq); | 243 | desc = irq_to_desc(irq); |
248 | for_each_cpu_and(cpu, data->old_domain, cpu_online_mask) { | 244 | for_each_cpu_and(cpu, data->old_domain, cpu_online_mask) { |
@@ -255,7 +251,6 @@ static void clear_irq_vector(int irq, struct apic_chip_data *data) | |||
255 | } | 251 | } |
256 | } | 252 | } |
257 | data->move_in_progress = 0; | 253 | data->move_in_progress = 0; |
258 | raw_spin_unlock_irqrestore(&vector_lock, flags); | ||
259 | } | 254 | } |
260 | 255 | ||
261 | void init_irq_alloc_info(struct irq_alloc_info *info, | 256 | void init_irq_alloc_info(struct irq_alloc_info *info, |
@@ -276,19 +271,24 @@ void copy_irq_alloc_info(struct irq_alloc_info *dst, struct irq_alloc_info *src) | |||
276 | static void x86_vector_free_irqs(struct irq_domain *domain, | 271 | static void x86_vector_free_irqs(struct irq_domain *domain, |
277 | unsigned int virq, unsigned int nr_irqs) | 272 | unsigned int virq, unsigned int nr_irqs) |
278 | { | 273 | { |
274 | struct apic_chip_data *apic_data; | ||
279 | struct irq_data *irq_data; | 275 | struct irq_data *irq_data; |
276 | unsigned long flags; | ||
280 | int i; | 277 | int i; |
281 | 278 | ||
282 | for (i = 0; i < nr_irqs; i++) { | 279 | for (i = 0; i < nr_irqs; i++) { |
283 | irq_data = irq_domain_get_irq_data(x86_vector_domain, virq + i); | 280 | irq_data = irq_domain_get_irq_data(x86_vector_domain, virq + i); |
284 | if (irq_data && irq_data->chip_data) { | 281 | if (irq_data && irq_data->chip_data) { |
282 | raw_spin_lock_irqsave(&vector_lock, flags); | ||
285 | clear_irq_vector(virq + i, irq_data->chip_data); | 283 | clear_irq_vector(virq + i, irq_data->chip_data); |
286 | free_apic_chip_data(irq_data->chip_data); | 284 | apic_data = irq_data->chip_data; |
285 | irq_domain_reset_irq_data(irq_data); | ||
286 | raw_spin_unlock_irqrestore(&vector_lock, flags); | ||
287 | free_apic_chip_data(apic_data); | ||
287 | #ifdef CONFIG_X86_IO_APIC | 288 | #ifdef CONFIG_X86_IO_APIC |
288 | if (virq + i < nr_legacy_irqs()) | 289 | if (virq + i < nr_legacy_irqs()) |
289 | legacy_irq_data[virq + i] = NULL; | 290 | legacy_irq_data[virq + i] = NULL; |
290 | #endif | 291 | #endif |
291 | irq_domain_reset_irq_data(irq_data); | ||
292 | } | 292 | } |
293 | } | 293 | } |
294 | } | 294 | } |