]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: riscv: Fix Spectre-v1 in APLIC interrupt handling
authorLukas Gerlach <lukas.gerlach@cispa.de>
Fri, 16 Jan 2026 09:57:31 +0000 (10:57 +0100)
committerAnup Patel <anup@brainfault.org>
Fri, 6 Mar 2026 05:50:30 +0000 (11:20 +0530)
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 commit 41b87599c743 ("KVM: arm/arm64: vgic: fix possible
spectre-v1 in vgic_get_irq()") and x86 commit 8c86405f606c ("KVM: x86:
Protect ioapic_read_indirect() from Spectre-v1/L1TF attacks").

Fixes: 74967aa208e2 ("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>
arch/riscv/kvm/aia_aplic.c

index d1e50bf5c3512cbc0a71eec9a1da976e39d45bd2..3464f3351df72db225108e0d745ed74574298acd 100644 (file)
@@ -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);