[PATCH] RISC-V: KVM: Fix integer overflow in kvm_pmu_validate_counter_mask()

Jiakai Xu xujiakai2025 at iscas.ac.cn
Thu Mar 19 18:02:21 PDT 2026


> > When a guest initiates an SBI_EXT_PMU_COUNTER_CFG_MATCH call with
> > ctr_base=0xfffffffffffffffe, ctr_mask=0xeb5f and flags=0x1
> > (SBI_PMU_CFG_FLAG_SKIP_MATCH), kvm_riscv_vcpu_pmu_ctr_cfg_match()
> > first invokes kvm_pmu_validate_counter_mask() to verify whether
> > ctr_base and ctr_mask are valid, by evaluating:
> >   !ctr_mask || (ctr_base + __fls(ctr_mask) >= kvm_pmu_num_counters(kvpmu))
> >
> > With the above inputs, __fls(0xeb5f) equals 15, and adding 15 to
> > 0xfffffffffffffffe causes an integer overflow, wrapping around to 13.
> > Since 13 is less than kvm_pmu_num_counters(), the validation wrongly
> > succeeds.
> >
> > Thereafter, since flags & SBI_PMU_CFG_FLAG_SKIP_MATCH is satisfied,
> > the code evaluates:
> >   !test_bit(ctr_base + __ffs(ctr_mask), kvpmu->pmc_in_use)
> >
> > Here __ffs(0xeb5f) equals 0, so test_bit() receives 0xfffffffffffffffe
> > as the bit index and attempts to access the corresponding element of
> > the kvpmu->pmc_in_use, which results in an invalid memory access. This
> > triggers the following Oops:
> >   Unable to handle kernel paging request at virtual address e3ebffff12abba89
> >    generic_test_bit include/asm-generic/bitops/generic-non-atomic.h:128
> >    kvm_riscv_vcpu_pmu_ctr_cfg_match arch/riscv/kvm/vcpu_pmu.c:758
> >    kvm_sbi_ext_pmu_handler arch/riscv/kvm/vcpu_sbi_pmu.c:49
> >    kvm_riscv_vcpu_sbi_ecall arch/riscv/kvm/vcpu_sbi.c:608
> >    kvm_riscv_vcpu_exit arch/riscv/kvm/vcpu_exit.c:240
> >
> > The root cause is that kvm_pmu_validate_counter_mask() does not account
> > for the case where ctr_base itself is out of range, allowing the
> > subsequent addition to silently overflow and bypass the check.
> >
> > Fix this by explicitly validating ctr_base against kvm_pmu_num_counters()
> > before performing the addition.
> >
> > This bug was found by fuzzing the KVM RISC-V PMU interface.
> 
> Thanks for fuzzing. Do you have a detailed report that you can share ?

Yes, here is the detailed crash report:

Unable to handle kernel paging request at virtual address e3ebffff12abba89
Current syz.4.5087 pgtable: 4K pagesize, 57-bit VAs, pgdp=0x0000000130cbc000
[e3ebffff12abba89] pgd=000000005fff6001, p4d=000000005fff5801, pud=000000005fff5401, pmd=000000005b4000e7
Oops [#1]
Modules linked in:
CPU: 3 UID: 0 PID: 5789 Comm: syz.4.5087 Tainted: G        W           7.0.0-rc2-00014-gc61ec3e8cc5d #1 PREEMPT 
Tainted: [W]=WARN
Hardware name: riscv-virtio,qemu (DT)
epc : generic_test_bit include/asm-generic/bitops/generic-non-atomic.h:128 [inline]
epc : kvm_riscv_vcpu_pmu_ctr_cfg_match+0x31a/0xe12 arch/riscv/kvm/vcpu_pmu.c:758
 ra : kvm_riscv_vcpu_pmu_ctr_cfg_match+0x2ee/0xe12 arch/riscv/kvm/vcpu_pmu.c:758
epc : ffffffff8012f320 ra : ffffffff8012f2f4 sp : ff200000067275c0
 gp : ffffffff8a382bc0 tp : ff60000095d03500 t0 : ff200000067276e0
 t1 : fffffffff3f3f3f3 t2 : 0000000200004520 s0 : ff200000067277c0
 s1 : ff20000006727740 a0 : 0000000000000007 a1 : 0000000000000000
 a2 : 0000000000080000 a3 : 03ec000012abba89 a4 : fffffffffffffffe
 a5 : e3ebffff12abba89 a6 : 0000000000000003 a7 : ffe3ffff00ce4ec4
 s2 : 1f600000955dd448 s3 : ff600000955dace0 s4 : 0000000000000001
 s5 : 0000000000000000 s6 : ff20000006727850 s7 : 0000000000000004
 s8 : fffffffffffffffe s9 : 0000000000000000 s10: 1fe4000000ce4ec4
 s11: 0000000000000003 t3 : cbdd65f600000000 t4 : 0000000000000000
 t5 : 0000000000000000 t6 : 0000000000000000 ssp : 0000000000000000
status: 0000000200000120 badaddr: e3ebffff12abba89 cause: 000000000000000d
[<ffffffff8012f320>] generic_test_bit include/asm-generic/bitops/generic-non-atomic.h:128 [inline]
[<ffffffff8012f320>] kvm_riscv_vcpu_pmu_ctr_cfg_match+0x31a/0xe12 arch/riscv/kvm/vcpu_pmu.c:758
[<ffffffff80136b12>] kvm_sbi_ext_pmu_handler+0x266/0x630 arch/riscv/kvm/vcpu_sbi_pmu.c:49
[<ffffffff80133636>] kvm_riscv_vcpu_sbi_ecall+0x11c/0x2f8 arch/riscv/kvm/vcpu_sbi.c:608
[<ffffffff80122e50>] kvm_riscv_vcpu_exit+0x7b2/0x9ba arch/riscv/kvm/vcpu_exit.c:240
[<ffffffff8011fa6e>] kvm_arch_vcpu_ioctl_run+0x13c6/0x3600 arch/riscv/kvm/vcpu.c:1008
[<ffffffff800da0ae>] kvm_vcpu_ioctl+0x532/0x13e0 virt/kvm/kvm_main.c:4476
[<ffffffff80d25228>] vfs_ioctl fs/ioctl.c:51 [inline]
[<ffffffff80d25228>] __do_sys_ioctl fs/ioctl.c:597 [inline]
[<ffffffff80d25228>] __se_sys_ioctl fs/ioctl.c:583 [inline]
[<ffffffff80d25228>] __riscv_sys_ioctl+0x180/0x1e4 fs/ioctl.c:583
[<ffffffff80078fc2>] syscall_handler+0x94/0x118 arch/riscv/include/asm/syscall.h:112
[<ffffffff86693a68>] do_trap_ecall_u+0x39e/0x62e arch/riscv/kernel/traps.c:344
[<ffffffff866be63e>] handle_exception+0x15e/0x16a arch/riscv/kernel/entry.S:232
Code: 9793 0036 993e 07b7 e000 17fd 5693 0039 1782 97b6 (c783) 0007 
---[ end trace 0000000000000000 ]---
----------------
Code disassembly (best guess):
   0:	00369793          	slli	a5,a3,0x3
   4:	993e                	add	s2,s2,a5
   6:	e00007b7          	lui	a5,0xe0000
   a:	17fd                	addi	a5,a5,-1 # 0xffffffffdfffffff
   c:	00395693          	srli	a3,s2,0x3
  10:	1782                	slli	a5,a5,0x20
  12:	97b6                	add	a5,a5,a3
* 14:	0007c783          	lbu	a5,0(a5) <-- trapping instruction

<<<<<<<<<<<<<<< tail report >>>>>>>>>>>>>>>

SYZFAIL: failed to recv rpc
fd=3 want=4 recv=0 n=0 (errno 9: Bad file descriptor)

<<<<<<<<<<<<<<< tail report >>>>>>>>>>>>>>>

> 
> > Fixes: 0cb74b65d2e5e6 ("RISC-V: KVM: Implement perf support without sampling")
> > Signed-off-by: Jiakai Xu <jiakaiPeanut at gmail.com>
> > Signed-off-by: Jiakai Xu <xujiakai2025 at iscas.ac.cn>
> > ---
> >   arch/riscv/kvm/vcpu_pmu.c | 6 ++++--
> >   1 file changed, 4 insertions(+), 2 deletions(-)
> >
> > diff --git a/arch/riscv/kvm/vcpu_pmu.c b/arch/riscv/kvm/vcpu_pmu.c
> > index e873430e596b2..a098a9b417ad8 100644
> > --- a/arch/riscv/kvm/vcpu_pmu.c
> > +++ b/arch/riscv/kvm/vcpu_pmu.c
> > @@ -266,8 +266,10 @@ static int pmu_ctr_read(struct kvm_vcpu *vcpu, unsigned long cidx,
> >   static int kvm_pmu_validate_counter_mask(struct kvm_pmu *kvpmu, unsigned long ctr_base,
> >   					 unsigned long ctr_mask)
> >   {
> > -	/* Make sure the we have a valid counter mask requested from the caller */
> > -	if (!ctr_mask || (ctr_base + __fls(ctr_mask) >= kvm_pmu_num_counters(kvpmu)))
> > +	unsigned long num_ctrs = kvm_pmu_num_counters(kvpmu);
> > +
> > +	/* Make sure we have a valid counter mask requested from the caller */
> > +	if (!ctr_mask || ctr_base >= num_ctrs || (ctr_base + __fls(ctr_mask) >= num_ctrs))
> >   		return -EINVAL;
> >   
> >   	return 0;
> 
> Thanks for the fix.
> 
> Reviewed-by: Atish Patra <atish.patra at linux.dev>

Thanks for the review!

Regards,
Jiakai




More information about the linux-riscv mailing list