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

Anup Patel <[email protected]>
Newsgroups org.infradead.lists.kvm-riscv,org.infradead.lists.linux-riscv,org.kernel.vger.kvm,org.kernel.vger.linux-kernel
Message-ID <CAAhSdy2s7TuchTcjw7S5HQ0kEtOX1zYevNh9H4jZghaU3hHcpw@mail.gmail.com>
On Thu, Jul 30, 2026 at 2:55 PM Guidong Han <[email protected]> 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 <[email protected]>
> Closes: https://lore.kernel.org/kvm/[email protected]/
> Fixes: e309fd113b9f ("RISC-V: KVM: Implement get event info function")
> Cc: [email protected]
> Signed-off-by: Guidong Han <[email protected]>

LGTM.

Reviewed-by: Anup Patel <[email protected]>

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/[email protected]/
> ---
>  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

-- 
kvm-riscv mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/kvm-riscv
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.