Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1d44b30f authored by Tomoki Sekiyama's avatar Tomoki Sekiyama Committed by Ingo Molnar
Browse files

x86/ioapic: Fix NULL pointer dereference on CPU hotplug after disabling irqs



In the current kernel, percpu variable `vector_irq' is not always
cleared when a CPU is offlined. If the CPU that has the disabled
irqs in vector_irq is hotplugged again, __setup_vector_irq()
hits invalid irq vector and may crash.

This bug can be reproduced as following;

 # echo 0 > /sys/devices/system/cpu/cpu7/online
 # modprobe -r some_driver_using_interrupts     # vector_irq@cpu7 uncleared
 # echo 1 > /sys/devices/system/cpu/cpu7/online # kernel may crash

To fix this problem, this patch clears vector_irq in
__fixup_irqs() when the CPU is offlined.

This also reverts commit f6175f5b, which partially fixes
this bug by clearing vector in __clear_irq_vector(). But in
environments with IOMMU IRQ remapper, it could fail because
cfg->domain doesn't contain offlined CPUs. With this patch, the
fix in __clear_irq_vector() can be reverted because every
vector_irq is already cleared in __fixup_irqs() on offlined CPUs.

Signed-off-by: default avatarTomoki Sekiyama <tomoki.sekiyama.qu@hitachi.com>
Acked-by: default avatarSuresh Siddha <suresh.b.siddha@intel.com>
Cc: yrl.pp-manager.tt@hitachi.com
Cc: Yinghai Lu <yinghai@kernel.org>
Cc: Alexander Gordeev <agordeev@redhat.com>
Link: http://lkml.kernel.org/r/20120726104732.2889.19144.stgit@kvmdev


Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent d431adfb
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -1204,7 +1204,7 @@ static void __clear_irq_vector(int irq, struct irq_cfg *cfg)
	BUG_ON(!cfg->vector);

	vector = cfg->vector;
	for_each_cpu(cpu, cfg->domain)
	for_each_cpu_and(cpu, cfg->domain, cpu_online_mask)
		per_cpu(vector_irq, cpu)[vector] = -1;

	cfg->vector = 0;
@@ -1212,7 +1212,7 @@ static void __clear_irq_vector(int irq, struct irq_cfg *cfg)

	if (likely(!cfg->move_in_progress))
		return;
	for_each_cpu(cpu, cfg->old_domain) {
	for_each_cpu_and(cpu, cfg->old_domain, cpu_online_mask) {
		for (vector = FIRST_EXTERNAL_VECTOR; vector < NR_VECTORS;
								vector++) {
			if (per_cpu(vector_irq, cpu)[vector] != irq)
+1 −0
Original line number Diff line number Diff line
@@ -328,6 +328,7 @@ void fixup_irqs(void)
				chip->irq_retrigger(data);
			raw_spin_unlock(&desc->lock);
		}
		__this_cpu_write(vector_irq[vector], -1);
	}
}
#endif