mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-03-22 07:27:12 +08:00
KVM: riscv: Fix Spectre-v1 in APLIC interrupt handling
Guests can control IRQ indices via MMIO. Sanitize them with array_index_nospec() to prevent speculative out-of-bounds access to the aplic->irqs[] array. Similar to arm64 commit41b87599c7("KVM: arm/arm64: vgic: fix possible spectre-v1 in vgic_get_irq()") and x86 commit8c86405f60("KVM: x86: Protect ioapic_read_indirect() from Spectre-v1/L1TF attacks"). Fixes:74967aa208("RISC-V: KVM: Add in-kernel emulation of AIA APLIC") Signed-off-by: Lukas Gerlach <lukas.gerlach@cispa.de> Reviewed-by: Nutty Liu <nutty.liu@hotmail.com> Reviewed-by: Anup Patel <anup@brainfault.org> Link: https://lore.kernel.org/r/20260116095731.24555-1-lukas.gerlach@cispa.de Signed-off-by: Anup Patel <anup@brainfault.org>
This commit is contained in:
committed by
Anup Patel
parent
11439c4635
commit
8565617a85
@@ -10,6 +10,7 @@
|
||||
#include <linux/irqchip/riscv-aplic.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/nospec.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/swab.h>
|
||||
#include <kvm/iodev.h>
|
||||
@@ -45,7 +46,7 @@ static u32 aplic_read_sourcecfg(struct aplic *aplic, u32 irq)
|
||||
|
||||
if (!irq || aplic->nr_irqs <= irq)
|
||||
return 0;
|
||||
irqd = &aplic->irqs[irq];
|
||||
irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];
|
||||
|
||||
raw_spin_lock_irqsave(&irqd->lock, flags);
|
||||
ret = irqd->sourcecfg;
|
||||
@@ -61,7 +62,7 @@ static void aplic_write_sourcecfg(struct aplic *aplic, u32 irq, u32 val)
|
||||
|
||||
if (!irq || aplic->nr_irqs <= irq)
|
||||
return;
|
||||
irqd = &aplic->irqs[irq];
|
||||
irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];
|
||||
|
||||
if (val & APLIC_SOURCECFG_D)
|
||||
val = 0;
|
||||
@@ -81,7 +82,7 @@ static u32 aplic_read_target(struct aplic *aplic, u32 irq)
|
||||
|
||||
if (!irq || aplic->nr_irqs <= irq)
|
||||
return 0;
|
||||
irqd = &aplic->irqs[irq];
|
||||
irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];
|
||||
|
||||
raw_spin_lock_irqsave(&irqd->lock, flags);
|
||||
ret = irqd->target;
|
||||
@@ -97,7 +98,7 @@ static void aplic_write_target(struct aplic *aplic, u32 irq, u32 val)
|
||||
|
||||
if (!irq || aplic->nr_irqs <= irq)
|
||||
return;
|
||||
irqd = &aplic->irqs[irq];
|
||||
irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];
|
||||
|
||||
val &= APLIC_TARGET_EIID_MASK |
|
||||
(APLIC_TARGET_HART_IDX_MASK << APLIC_TARGET_HART_IDX_SHIFT) |
|
||||
@@ -116,7 +117,7 @@ static bool aplic_read_pending(struct aplic *aplic, u32 irq)
|
||||
|
||||
if (!irq || aplic->nr_irqs <= irq)
|
||||
return false;
|
||||
irqd = &aplic->irqs[irq];
|
||||
irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];
|
||||
|
||||
raw_spin_lock_irqsave(&irqd->lock, flags);
|
||||
ret = (irqd->state & APLIC_IRQ_STATE_PENDING) ? true : false;
|
||||
@@ -132,7 +133,7 @@ static void aplic_write_pending(struct aplic *aplic, u32 irq, bool pending)
|
||||
|
||||
if (!irq || aplic->nr_irqs <= irq)
|
||||
return;
|
||||
irqd = &aplic->irqs[irq];
|
||||
irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];
|
||||
|
||||
raw_spin_lock_irqsave(&irqd->lock, flags);
|
||||
|
||||
@@ -170,7 +171,7 @@ static bool aplic_read_enabled(struct aplic *aplic, u32 irq)
|
||||
|
||||
if (!irq || aplic->nr_irqs <= irq)
|
||||
return false;
|
||||
irqd = &aplic->irqs[irq];
|
||||
irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];
|
||||
|
||||
raw_spin_lock_irqsave(&irqd->lock, flags);
|
||||
ret = (irqd->state & APLIC_IRQ_STATE_ENABLED) ? true : false;
|
||||
@@ -186,7 +187,7 @@ static void aplic_write_enabled(struct aplic *aplic, u32 irq, bool enabled)
|
||||
|
||||
if (!irq || aplic->nr_irqs <= irq)
|
||||
return;
|
||||
irqd = &aplic->irqs[irq];
|
||||
irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];
|
||||
|
||||
raw_spin_lock_irqsave(&irqd->lock, flags);
|
||||
if (enabled)
|
||||
@@ -205,7 +206,7 @@ static bool aplic_read_input(struct aplic *aplic, u32 irq)
|
||||
|
||||
if (!irq || aplic->nr_irqs <= irq)
|
||||
return false;
|
||||
irqd = &aplic->irqs[irq];
|
||||
irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];
|
||||
|
||||
raw_spin_lock_irqsave(&irqd->lock, flags);
|
||||
|
||||
@@ -254,7 +255,7 @@ static void aplic_update_irq_range(struct kvm *kvm, u32 first, u32 last)
|
||||
for (irq = first; irq <= last; irq++) {
|
||||
if (!irq || aplic->nr_irqs <= irq)
|
||||
continue;
|
||||
irqd = &aplic->irqs[irq];
|
||||
irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];
|
||||
|
||||
raw_spin_lock_irqsave(&irqd->lock, flags);
|
||||
|
||||
@@ -283,7 +284,7 @@ int kvm_riscv_aia_aplic_inject(struct kvm *kvm, u32 source, bool level)
|
||||
|
||||
if (!aplic || !source || (aplic->nr_irqs <= source))
|
||||
return -ENODEV;
|
||||
irqd = &aplic->irqs[source];
|
||||
irqd = &aplic->irqs[array_index_nospec(source, aplic->nr_irqs)];
|
||||
ie = (aplic->domaincfg & APLIC_DOMAINCFG_IE) ? true : false;
|
||||
|
||||
raw_spin_lock_irqsave(&irqd->lock, flags);
|
||||
|
||||
Reference in New Issue
Block a user