* [PATCH v2 1/2] RISC-V: KVM: Validate AIA MMIO address ranges
2026-09-20 13:08 [PATCH v2 0/2] RISC-V: KVM: Validate AIA MMIO ranges against GPA width Pengpeng Hou
@ 2026-09-20 13:09 ` Pengpeng Hou
2026-09-20 13:09 ` [PATCH v2 2/2] RISC-V: KVM: Disallow GPA-width changes after AIA init Pengpeng Hou
1 sibling, 0 replies; 3+ messages in thread
From: Pengpeng Hou @ 2026-09-20 13:09 UTC (permalink / raw)
To: anup
Cc: Pengpeng Hou, Alexandre Ghiti, Andrew Jones, Albert Ou,
Atish Patra, Fangyu Yu, Guo Ren, kvm-riscv, kvm, linux-kernel,
linux-riscv, Nutty Liu, Palmer Dabbelt, Paul Walmsley
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@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;
+ }
+
/* Group index bits must not overlap guest and HART index bits. */
if (aia->nr_group_bits &&
aia->nr_group_shift < (IMSIC_MMIO_PAGE_SHIFT +
^ permalink raw reply [flat|nested] 3+ messages in thread* [PATCH v2 2/2] RISC-V: KVM: Disallow GPA-width changes after AIA init
2026-09-20 13:08 [PATCH v2 0/2] RISC-V: KVM: Validate AIA MMIO ranges against GPA width Pengpeng Hou
2026-09-20 13:09 ` [PATCH v2 1/2] RISC-V: KVM: Validate AIA MMIO address ranges Pengpeng Hou
@ 2026-09-20 13:09 ` Pengpeng Hou
1 sibling, 0 replies; 3+ messages in thread
From: Pengpeng Hou @ 2026-09-20 13:09 UTC (permalink / raw)
To: anup
Cc: Pengpeng Hou, Alexandre Ghiti, Andrew Jones, Albert Ou,
Atish Patra, Fangyu Yu, Guo Ren, kvm-riscv, kvm, linux-kernel,
linux-riscv, Nutty Liu, Palmer Dabbelt, Paul Walmsley
KVM_ENABLE_CAP(KVM_CAP_VM_GPA_BITS) allows userspace to reduce the
stage-2 GPA width while a VM has no vCPUs or memory slots.
AIA initialization can complete with an APLIC and no vCPUs or memory
slots. The APLIC MMIO device has then been registered, but the existing
checks still allow userspace to shrink the GPA width below its address.
Reject GPA-width changes after AIA initialization. Both paths hold
kvm->lock, serializing the width change with AIA initialization.
The issue was found by our static-analysis tool.
Fixes: 7263b4fdb0b2 ("RISC-V: KVM: Reuse KVM_CAP_VM_GPA_BITS to select HGATP.MODE")
Reviewed-by: Anup Patel <anup@brainfault.org>
Assisted-by: gpt 5
Signed-off-by: Pengpeng Hou <hppiscas@163.com>
---
Changes since v1: https://lore.kernel.org/r/4852f3b8985c353811fa065e4701d76dc9a7f086.1786512671.git.pengpeng@iscas.ac.cn/
- No change to the code; retain Anup's Reviewed-by.
- Rebase and clarify the locking description.
arch/riscv/kvm/vm.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/riscv/kvm/vm.c b/arch/riscv/kvm/vm.c
index a9f083f..66edfaa 100644
--- a/arch/riscv/kvm/vm.c
+++ b/arch/riscv/kvm/vm.c
@@ -250,7 +250,8 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap)
mutex_lock(&kvm->lock);
mutex_lock(&kvm->slots_lock);
- if (kvm->created_vcpus || !kvm_are_all_memslots_empty(kvm))
+ if (kvm->created_vcpus || !kvm_are_all_memslots_empty(kvm) ||
+ kvm_riscv_aia_initialized(kvm))
r = -EBUSY;
else
kvm->arch.pgd_levels = new_levels;
^ permalink raw reply [flat|nested] 3+ messages in thread