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

Anup Patel anup at brainfault.org
Fri Sep 11 00:24:22 PDT 2026


On Wed, Aug 12, 2026 at 11:09 AM Pengpeng Hou <pengpeng at iscas.ac.cn> wrote:
>
> Userspace supplies the APLIC and per-vCPU IMSIC base addresses through
> the AIA device API.  The address setters currently reject only
> misaligned addresses.
>
> The regions are later registered on KVM's MMIO bus with fixed sizes.  An
> aligned base near U64_MAX can therefore wrap when the range end is
> formed.  A lower base can still place the range beyond the selected
> guest stage-2 physical address space.  kvm_io_bus_cmp() assumes that
> registered ranges do not wrap.
>
> Validate the complete interval when an address is stored.  Since
> KVM_CAP_VM_GPA_BITS can change the selected GPA width before AIA
> initialization, validate every address again before registering the
> first MMIO device.
>
> Return -EINVAL for misalignment or arithmetic overflow and -E2BIG when
> the end exceeds the stage-2 GPA limit, following the arm64 VGIC
> address-validation convention.
>
> Fixes: 89d01306e34d ("RISC-V: KVM: Implement device interface for AIA irqchip")
> Signed-off-by: Pengpeng Hou <pengpeng at iscas.ac.cn>
> ---
>  arch/riscv/kvm/aia_device.c | 79 +++++++++++++++++++++++++++++++------
>  1 file changed, 66 insertions(+), 13 deletions(-)
>
> diff --git a/arch/riscv/kvm/aia_device.c b/arch/riscv/kvm/aia_device.c
> index efc7c0bcfba9..e969dbdc4b97 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);
> @@ -191,6 +216,40 @@ static int aia_imsic_addr(struct kvm *kvm, u64 *addr,
>         return 0;
>  }
>
> +static int aia_validate_addr_ranges(struct kvm *kvm)
> +{
> +       struct kvm_aia *aia = &kvm->arch.aia;
> +       struct kvm_vcpu_aia *vaia;
> +       struct kvm_vcpu *vcpu;
> +       unsigned long idx;
> +       int ret;
> +
> +       if (aia->nr_sources) {
> +               if (aia->aplic_addr == KVM_RISCV_AIA_UNDEF_ADDR)
> +                       return -EINVAL;
> +
> +               ret = aia_check_addr_range(kvm, aia->aplic_addr,
> +                                          KVM_DEV_RISCV_APLIC_ALIGN,
> +                                          KVM_DEV_RISCV_APLIC_SIZE);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       kvm_for_each_vcpu(idx, vcpu, kvm) {
> +               vaia = &vcpu->arch.aia_context;
> +               if (vaia->imsic_addr == KVM_RISCV_AIA_UNDEF_ADDR)
> +                       return -EINVAL;
> +
> +               ret = aia_check_addr_range(kvm, vaia->imsic_addr,
> +                                          KVM_DEV_RISCV_IMSIC_ALIGN,
> +                                          KVM_DEV_RISCV_IMSIC_SIZE);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       return 0;
> +}
> +
>  static gpa_t aia_imsic_ppn(struct kvm_aia *aia, gpa_t addr)
>  {
>         u32 h, l;
> @@ -244,9 +303,9 @@ static int aia_init(struct kvm *kvm)
>         if (aia->nr_ids < aia->nr_sources)
>                 return -EINVAL;
>
> -       /* APLIC base is required for non-zero number of sources */
> -       if (aia->nr_sources && aia->aplic_addr == KVM_RISCV_AIA_UNDEF_ADDR)
> -               return -EINVAL;
> +       ret = aia_validate_addr_ranges(kvm);
> +       if (ret)
> +               return ret;

No need for this change.

>
>         /* Group index bits must not overlap guest and HART index bits. */
>         if (aia->nr_group_bits &&
> @@ -263,12 +322,6 @@ static int aia_init(struct kvm *kvm)
>         kvm_for_each_vcpu(idx, vcpu, kvm) {
>                 vaia = &vcpu->arch.aia_context;
>
> -               /* IMSIC base is required */
> -               if (vaia->imsic_addr == KVM_RISCV_AIA_UNDEF_ADDR) {
> -                       ret = -EINVAL;
> -                       goto fail_cleanup_imsics;
> -               }
> -

Don't drop this check.

>                 /* All IMSICs should have matching base PPN */
>                 if (base_ppn == KVM_RISCV_AIA_UNDEF_ADDR)
>                         base_ppn = aia_imsic_ppn(aia, vaia->imsic_addr);
> --
> 2.50.1 (Apple Git-155)
>

Regards,
Anup



More information about the linux-riscv mailing list