[PATCH v2 1/2] RISC-V: KVM: Validate AIA MMIO address ranges

Anup Patel anup at brainfault.org
Thu Nov 12 21:36:45 PST 2026


On Sun, Sep 20, 2026 at 6:39 PM Pengpeng Hou <hppiscas at 163.com> wrote:
>
> The AIA address setters check alignment but not the complete fixed-size
> APLIC and IMSIC MMIO regions. A range can therefore wrap when its end is
> formed or extend beyond the selected guest physical address space before
> it is registered on the KVM MMIO bus.
>
> Check the complete range in both setters. Return -EINVAL for alignment
> or arithmetic overflow and -E2BIG for a range beyond the selected GPA
> limit, following the arm64 VGIC address-validation convention.
>
> Recheck only the APLIC range during initialization: userspace can set
> its address and then change KVM_CAP_VM_GPA_BITS while the VM still has
> no vCPUs or memory slots. An IMSIC address, in contrast, can only be set
> for an existing vCPU, which already prevents subsequent GPA-width
> changes. No additional pass over the vCPUs is needed.
>
> Keep the existing required-address checks and IMSIC cleanup path.
>
> The issue was found by our static-analysis tool.
>
> Fixes: 89d01306e34d ("RISC-V: KVM: Implement device interface for AIA irqchip")
> Assisted-by: gpt 5
> Signed-off-by: Pengpeng Hou <hppiscas at 163.com>
> ---
> Changes since v1: https://lore.kernel.org/r/38f87205419f328693bc6ab25ae78f021c2dd003.1786512671.git.pengpeng@iscas.ac.cn/
> - Keep the original APLIC and per-vCPU IMSIC required-address checks.
> - Drop the separate pass over all vCPUs during initialization.
> - Recheck only APLIC, whose address can be configured before GPA width
>   becomes immutable; IMSIC setters already require an existing vCPU.
>
>  arch/riscv/kvm/aia_device.c | 42 +++++++++++++++++++++++++++++++++----
>  1 file changed, 38 insertions(+), 4 deletions(-)
>
> diff --git a/arch/riscv/kvm/aia_device.c b/arch/riscv/kvm/aia_device.c
> index efc7c0b..4490015 100644
> --- a/arch/riscv/kvm/aia_device.c
> +++ b/arch/riscv/kvm/aia_device.c
> @@ -10,7 +10,9 @@
>  #include <linux/bits.h>
>  #include <linux/irqchip/riscv-imsic.h>
>  #include <linux/kvm_host.h>
> +#include <linux/overflow.h>
>  #include <linux/uaccess.h>
> +#include <asm/kvm_gstage.h>
>  #include <asm/kvm_isa.h>
>
>  static int aia_create(struct kvm_device *dev, u32 type)
> @@ -142,17 +144,37 @@ static int aia_config(struct kvm *kvm, unsigned long type,
>         return 0;
>  }
>
> +static int aia_check_addr_range(struct kvm *kvm, u64 addr, u64 alignment,
> +                               u64 size)
> +{
> +       u64 end;
> +
> +       if (!IS_ALIGNED(addr, alignment))
> +               return -EINVAL;
> +
> +       if (check_add_overflow(addr, size, &end))
> +               return -EINVAL;
> +
> +       if (end > kvm_riscv_gstage_gpa_size(kvm->arch.pgd_levels))
> +               return -E2BIG;
> +
> +       return 0;
> +}
> +
>  static int aia_aplic_addr(struct kvm *kvm, u64 *addr, bool write)
>  {
>         struct kvm_aia *aia = &kvm->arch.aia;
> +       int ret;
>
>         if (write) {
>                 /* Writes can only be done before irqchip is initialized */
>                 if (kvm_riscv_aia_initialized(kvm))
>                         return -EBUSY;
>
> -               if (*addr & (KVM_DEV_RISCV_APLIC_ALIGN - 1))
> -                       return -EINVAL;
> +               ret = aia_check_addr_range(kvm, *addr, KVM_DEV_RISCV_APLIC_ALIGN,
> +                                          KVM_DEV_RISCV_APLIC_SIZE);
> +               if (ret)
> +                       return ret;
>
>                 aia->aplic_addr = *addr;
>         } else
> @@ -166,6 +188,7 @@ static int aia_imsic_addr(struct kvm *kvm, u64 *addr,
>  {
>         struct kvm_vcpu *vcpu;
>         struct kvm_vcpu_aia *vcpu_aia;
> +       int ret;
>
>         vcpu = kvm_get_vcpu(kvm, vcpu_idx);
>         if (!vcpu)
> @@ -177,8 +200,10 @@ static int aia_imsic_addr(struct kvm *kvm, u64 *addr,
>                 if (kvm_riscv_aia_initialized(kvm))
>                         return -EBUSY;
>
> -               if (*addr & (KVM_DEV_RISCV_IMSIC_ALIGN - 1))
> -                       return -EINVAL;
> +               ret = aia_check_addr_range(kvm, *addr, KVM_DEV_RISCV_IMSIC_ALIGN,
> +                                          KVM_DEV_RISCV_IMSIC_SIZE);
> +               if (ret)
> +                       return ret;
>         }
>
>         mutex_lock(&vcpu->mutex);
> @@ -248,6 +273,15 @@ static int aia_init(struct kvm *kvm)
>         if (aia->nr_sources && aia->aplic_addr == KVM_RISCV_AIA_UNDEF_ADDR)
>                 return -EINVAL;
>
> +       /* The GPA width may change after setting APLIC's address on an empty VM. */
> +       if (aia->nr_sources) {
> +               ret = aia_check_addr_range(kvm, aia->aplic_addr,
> +                                          KVM_DEV_RISCV_APLIC_ALIGN,
> +                                          KVM_DEV_RISCV_APLIC_SIZE);
> +               if (ret)
> +                       return ret;
> +       }
> +

We should also check IMSIC addresses one more time in aia_init(). I will
take care of it at the time of merging.

>         /* Group index bits must not overlap guest and HART index bits. */
>         if (aia->nr_group_bits &&
>             aia->nr_group_shift < (IMSIC_MMIO_PAGE_SHIFT +
>

LGTM.

Reviewed-by: Anup Patel <anup at brainfault.org>

Queued this patch as a fix for Linux-7.4-rcX

Thanks,
Anup



More information about the linux-riscv mailing list