[PATCH 1/2] RISC-V: KVM: Validate AIA MMIO address ranges
Pengpeng Hou
pengpeng at iscas.ac.cn
Tue Aug 11 22:39:31 PDT 2026
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;
/* 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;
- }
-
/* 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)
More information about the kvm-riscv
mailing list