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

From: Anup Patel

Date: Fri Sep 11 2026 - 03:26:42 EST


On Wed, Aug 12, 2026 at 11:09 AM Pengpeng Hou <pengpeng@xxxxxxxxxxx> 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@xxxxxxxxxxx>
> ---
> 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