[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