From b86997f39d4fabca766ef7bd017223fa2ced58e8 Mon Sep 17 00:00:00 2001 From: Jiakai Xu Date: Tue, 24 Feb 2026 10:44:38 +0000 Subject: [PATCH] RISC-V: KVM: Fix use-after-free in kvm_riscv_aia_aplic_has_attr() Fuzzer reports a KASAN use-after-free bug triggered by a race between KVM_HAS_DEVICE_ATTR and KVM_SET_DEVICE_ATTR ioctls on the AIA device. The root cause is that aia_has_attr() invokes kvm_riscv_aia_aplic_has_attr() without holding dev->kvm->lock, while a concurrent aia_set_attr() may call aia_init() under that lock. When aia_init() fails after kvm_riscv_aia_aplic_init() has succeeded, it calls kvm_riscv_aia_aplic_cleanup() in its fail_cleanup_imsics path, which frees both aplic_state and aplic_state->irqs. The concurrent has_attr path can then dereference the freed aplic->irqs in aplic_read_pending(): irqd = &aplic->irqs[irq]; /* UAF here */ KASAN report: BUG: KASAN: slab-use-after-free in aplic_read_pending arch/riscv/kvm/aia_aplic.c:119 [inline] BUG: KASAN: slab-use-after-free in aplic_read_pending_word arch/riscv/kvm/aia_aplic.c:351 [inline] BUG: KASAN: slab-use-after-free in aplic_mmio_read_offset arch/riscv/kvm/aia_aplic.c:406 Read of size 8 at addr ff600000ba965d58 by task 9498 Call Trace: aplic_read_pending arch/riscv/kvm/aia_aplic.c:119 [inline] aplic_read_pending_word arch/riscv/kvm/aia_aplic.c:351 [inline] aplic_mmio_read_offset arch/riscv/kvm/aia_aplic.c:406 kvm_riscv_aia_aplic_has_attr arch/riscv/kvm/aia_aplic.c:566 aia_has_attr arch/riscv/kvm/aia_device.c:469 allocated by task 9473: kvm_riscv_aia_aplic_init arch/riscv/kvm/aia_aplic.c:583 aia_init arch/riscv/kvm/aia_device.c:248 [inline] aia_set_attr arch/riscv/kvm/aia_device.c:334 freed by task 9473: kvm_riscv_aia_aplic_cleanup arch/riscv/kvm/aia_aplic.c:644 aia_init arch/riscv/kvm/aia_device.c:292 [inline] aia_set_attr arch/riscv/kvm/aia_device.c:334 The patch replaces the actual MMIO read in kvm_riscv_aia_aplic_has_attr() with a new aplic_mmio_has_offset() that only validates whether the given offset falls within a known APLIC region, without touching any dynamically allocated state. This is consistent with the KVM API documentation for KVM_HAS_DEVICE_ATTR: "Tests whether a device supports a particular attribute. A successful return indicates the attribute is implemented. It does not necessarily indicate that the attribute can be read or written in the device's current state." The upper bounds of each region are taken directly from the RISC-V AIA specification, so the check is independent of the runtime values of nr_irqs and nr_words. This patch both fixes the use-after-free and makes the has_attr implementation semantically correct. Fixes: 289a007b98b06d ("RISC-V: KVM: Expose APLIC registers as attributes of AIA irqchip") Signed-off-by: Jiakai Xu Signed-off-by: Jiakai Xu Signed-off-by: Linux RISC-V bot --- arch/riscv/kvm/aia_aplic.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/arch/riscv/kvm/aia_aplic.c b/arch/riscv/kvm/aia_aplic.c index f59d1c0c8c43a7..5e7a1055b2de6a 100644 --- a/arch/riscv/kvm/aia_aplic.c +++ b/arch/riscv/kvm/aia_aplic.c @@ -527,6 +527,31 @@ static struct kvm_io_device_ops aplic_iodoev_ops = { .write = aplic_mmio_write, }; +static int aplic_mmio_has_offset(struct kvm *kvm, gpa_t off) +{ + if ((off & 0x3) != 0) + return -EOPNOTSUPP; + + if ((off == APLIC_DOMAINCFG) || + (off >= APLIC_SOURCECFG_BASE && off < (APLIC_SOURCECFG_BASE + 1023 * 4)) || + (off >= APLIC_SETIP_BASE && off < (APLIC_SETIP_BASE + 32 * 4)) || + (off == APLIC_SETIPNUM) || + (off >= APLIC_CLRIP_BASE && off < (APLIC_CLRIP_BASE + 32 * 4)) || + (off == APLIC_CLRIPNUM) || + (off >= APLIC_SETIE_BASE && off < (APLIC_SETIE_BASE + 32 * 4)) || + (off == APLIC_SETIENUM) || + (off >= APLIC_CLRIE_BASE && off < (APLIC_CLRIE_BASE + 32 * 4)) || + (off == APLIC_CLRIENUM) || + (off == APLIC_SETIPNUM_LE) || + (off == APLIC_SETIPNUM_BE) || + (off == APLIC_GENMSI) || + (off >= APLIC_TARGET_BASE && off < (APLIC_TARGET_BASE + 1203 * 4)) + ) + return 0; + else + return -ENODEV; +} + int kvm_riscv_aia_aplic_set_attr(struct kvm *kvm, unsigned long type, u32 v) { int rc; @@ -558,12 +583,11 @@ int kvm_riscv_aia_aplic_get_attr(struct kvm *kvm, unsigned long type, u32 *v) int kvm_riscv_aia_aplic_has_attr(struct kvm *kvm, unsigned long type) { int rc; - u32 val; if (!kvm->arch.aia.aplic_state) return -ENODEV; - rc = aplic_mmio_read_offset(kvm, type, &val); + rc = aplic_mmio_has_offset(kvm, type); if (rc) return rc;