[PATCH v2 RESEND] RISC-V: KVM: Fix PMU event info array size overflow

Anup Patel anup at brainfault.org
Sat Aug 8 06:34:41 PDT 2026


On Thu, Jul 30, 2026 at 2:55 PM Guidong Han <2045gemini at gmail.com> wrote:
>
> SBI PMU EVENT_GET_INFO stores guest-controlled
> num_events * sizeof(*einfo) in an int. On RV64, num_events = 0x10000001
> makes 0x100000010 truncate to 16. KVM then allocates one entry but loops
> over the original num_events, causing out-of-bounds reads and writes. A
> nested guest triggered:
>
> BUG: KASAN: slab-out-of-bounds in kvm_riscv_vcpu_pmu_event_info+0xa4/0x142
> Read of size 4 at addr ff600000074d46b0 by task init/1
> Call Trace:
> [<ffffffff8006471c>] kvm_riscv_vcpu_pmu_event_info+0xa4/0x142
> [<ffffffff800690c0>] kvm_sbi_ext_pmu_handler+0xca/0x268
> [<ffffffff8006779e>] kvm_riscv_vcpu_sbi_ecall+0xec/0x1e6
> [<ffffffff8006008c>] kvm_riscv_vcpu_exit+0x48c/0x540
> [<ffffffff8005ea0a>] kvm_arch_vcpu_ioctl_run+0x37e/0xc80
> Allocated by task 1:
>  __kmalloc_noprof+0x19e/0x4b0
>  kvm_riscv_vcpu_pmu_event_info+0x72/0x142
>  kvm_sbi_ext_pmu_handler+0xca/0x268
>  kvm_riscv_vcpu_sbi_ecall+0xec/0x1e6
>  kvm_riscv_vcpu_exit+0x48c/0x540
>  kvm_arch_vcpu_ioctl_run+0x37e/0xc80
> The buggy address is located 0 bytes to the right of
>  allocated 16-byte region [ff600000074d46a0, ff600000074d46b0)
>
> Store the shared-memory size in size_t and reject multiplication overflow.
> Allocate the guest-driven array with GFP_KERNEL_ACCOUNT so it is charged
> to kmemcg, and use __GFP_NOWARN to suppress allocation failure warnings.
> Use kvcalloc() to allow vmalloc fallback and an unsigned long loop index
> to match num_events.
>
> Reported-by: Naveed Khan <naveed at digiscrypt.com>
> Closes: https://lore.kernel.org/kvm/178345245327.72065.13249716450708539854@digiscrypt.com/
> Fixes: e309fd113b9f ("RISC-V: KVM: Implement get event info function")
> Cc: stable at vger.kernel.org
> Signed-off-by: Guidong Han <2045gemini at gmail.com>

LGTM.

Reviewed-by: Anup Patel <anup at brainfault.org>

Queued this patch for Linux-7.3

Thanks,
Anup

> ---
> RESEND:
> - Send v2 as a new top-level thread instead of replying to v1.
>
> Changes in v2:
> - Use an accounted, no-warning kvcalloc() allocation and kvfree(), as
>   suggested by Sashiko.
> - Naveed Khan reported the issue before v1. I missed his report at the
>   time, so add Reported-by and Closes tags to credit him.
>
> Link to v1: https://lore.kernel.org/kvm/20260718153620.829378-1-2045gemini@gmail.com/
> ---
>  arch/riscv/kvm/vcpu_pmu.c | 14 +++++++++-----
>  1 file changed, 9 insertions(+), 5 deletions(-)
>
> diff --git a/arch/riscv/kvm/vcpu_pmu.c b/arch/riscv/kvm/vcpu_pmu.c
> index bb46dcbfb24d..8e1f6abb84e2 100644
> --- a/arch/riscv/kvm/vcpu_pmu.c
> +++ b/arch/riscv/kvm/vcpu_pmu.c
> @@ -12,7 +12,9 @@
>  #include <linux/err.h>
>  #include <linux/kvm_host.h>
>  #include <linux/nospec.h>
> +#include <linux/overflow.h>
>  #include <linux/perf/riscv_pmu.h>
> +#include <linux/slab.h>
>  #include <asm/csr.h>
>  #include <asm/kvm_isa.h>
>  #include <asm/kvm_vcpu_sbi.h>
> @@ -479,13 +481,14 @@ int kvm_riscv_vcpu_pmu_event_info(struct kvm_vcpu *vcpu, unsigned long saddr_low
>                                   unsigned long flags, struct kvm_vcpu_sbi_return *retdata)
>  {
>         struct riscv_pmu_event_info *einfo = NULL;
> -       int shmem_size = num_events * sizeof(*einfo);
> +       size_t shmem_size;
>         gpa_t shmem;
>         u32 eidx, etype;
>         u64 econfig;
>         int ret;
>
> -       if (flags != 0 || (saddr_low & (SZ_16 - 1) || num_events == 0)) {
> +       if (flags != 0 || (saddr_low & (SZ_16 - 1)) || num_events == 0 ||
> +           check_mul_overflow(num_events, sizeof(*einfo), &shmem_size)) {
>                 ret = SBI_ERR_INVALID_PARAM;
>                 goto out;
>         }
> @@ -500,7 +503,8 @@ int kvm_riscv_vcpu_pmu_event_info(struct kvm_vcpu *vcpu, unsigned long saddr_low
>                 }
>         }
>
> -       einfo = kzalloc(shmem_size, GFP_KERNEL);
> +       einfo = kvcalloc(num_events, sizeof(*einfo),
> +                        GFP_KERNEL_ACCOUNT | __GFP_NOWARN);
>         if (!einfo) {
>                 ret = SBI_ERR_FAILURE;
>                 goto out;
> @@ -512,7 +516,7 @@ int kvm_riscv_vcpu_pmu_event_info(struct kvm_vcpu *vcpu, unsigned long saddr_low
>                 goto free_mem;
>         }
>
> -       for (int i = 0; i < num_events; i++) {
> +       for (unsigned long i = 0; i < num_events; i++) {
>                 eidx = einfo[i].event_idx;
>                 etype = kvm_pmu_get_perf_event_type(eidx);
>                 econfig = kvm_pmu_get_perf_event_config(eidx, einfo[i].event_data);
> @@ -525,7 +529,7 @@ int kvm_riscv_vcpu_pmu_event_info(struct kvm_vcpu *vcpu, unsigned long saddr_low
>                 ret = SBI_ERR_INVALID_ADDRESS;
>
>  free_mem:
> -       kfree(einfo);
> +       kvfree(einfo);
>  out:
>         retdata->err_val = ret;
>
> --
> 2.43.0



More information about the kvm-riscv mailing list