[PATCH] KVM: riscv: Fix Spectre-v1 in APLIC interrupt handling

Nutty.Liu nutty.liu at hotmail.com
Mon Jan 19 01:59:19 PST 2026


On 1/16/2026 5:57 PM, Lukas Gerlach wrote:
> 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 at cispa.de>
> ---
>   arch/riscv/kvm/aia_aplic.c | 23 ++++++++++++-----------
>   1 file changed, 12 insertions(+), 11 deletions(-)
>
> diff --git a/arch/riscv/kvm/aia_aplic.c b/arch/riscv/kvm/aia_aplic.c
> index f59d1c0c8c43..a2b831e57ecd 100644
> --- a/arch/riscv/kvm/aia_aplic.c
> +++ b/arch/riscv/kvm/aia_aplic.c
> @@ -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;
Seems the above code would be better as follows ?
                             if (!irq)
continue;
                             if(aplic->nr_irqs <= irq)
                                     return;

Otherwise,
Reviewed-by: Nutty Liu <nutty.liu at hotmail.com>

Thanks,
Nutty
> -		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);
> --
> 2.51.0
>
>



More information about the linux-riscv mailing list