| Message ID | 20260318092956.708246-1-xujiakai2025@iscas.ac.cn |
|---|---|
| State | Accepted |
| Headers | show |
| Series | RISC-V: KVM: Fix double-free of sdata in kvm_pmu_clear_snapshot_area() | expand |
On Wed, Mar 18, 2026 at 09:29:56AM +0000, Jiakai Xu wrote: > In kvm_riscv_vcpu_pmu_snapshot_set_shmem(), when kvm_vcpu_write_guest() > fails, kvpmu->sdata is freed but not set to NULL. This leaves a dangling > pointer that will be freed again when kvm_pmu_clear_snapshot_area() is > called during vcpu teardown, triggering a KASAN double-free report. > > First free occurs in kvm_riscv_vcpu_pmu_snapshot_set_shmem(): > kvm_riscv_vcpu_pmu_snapshot_set_shmem arch/riscv/kvm/vcpu_pmu.c:443 > kvm_sbi_ext_pmu_handler arch/riscv/kvm/vcpu_sbi_pmu.c:74 > kvm_riscv_vcpu_sbi_ecall arch/riscv/kvm/vcpu_sbi.c:608 > kvm_riscv_vcpu_exit arch/riscv/kvm/vcpu_exit.c:240 > kvm_arch_vcpu_ioctl_run arch/riscv/kvm/vcpu.c:1008 > kvm_vcpu_ioctl virt/kvm/kvm_main.c:4476 > > Second free (double-free) occurs in kvm_pmu_clear_snapshot_area(): > kvm_pmu_clear_snapshot_area arch/riscv/kvm/vcpu_pmu.c:403 [inline] > kvm_riscv_vcpu_pmu_deinit.part arch/riscv/kvm/vcpu_pmu.c:905 > kvm_riscv_vcpu_pmu_deinit arch/riscv/kvm/vcpu_pmu.c:893 > kvm_arch_vcpu_destroy arch/riscv/kvm/vcpu.c:199 > kvm_vcpu_destroy virt/kvm/kvm_main.c:469 [inline] > kvm_destroy_vcpus virt/kvm/kvm_main.c:489 > kvm_arch_destroy_vm arch/riscv/kvm/vm.c:54 > kvm_destroy_vm virt/kvm/kvm_main.c:1301 [inline] > kvm_put_kvm virt/kvm/kvm_main.c:1338 > kvm_vm_release virt/kvm/kvm_main.c:1361 > > Fix it by setting kvpmu->sdata to NULL after kfree() in > kvm_riscv_vcpu_pmu_snapshot_set_shmem(), so that the subsequent > kfree(NULL) in kvm_pmu_clear_snapshot_area() becomes a safe no-op. > > This bug was found by fuzzing the KVM RISC-V PMU interface. > > Fixes: c2f41ddbcdd756 ("RISC-V: KVM: Implement SBI PMU Snapshot feature") > Signed-off-by: Jiakai Xu <jiakaiPeanut@gmail.com> > Signed-off-by: Jiakai Xu <xujiakai2025@iscas.ac.cn> > --- > arch/riscv/kvm/vcpu_pmu.c | 1 + > 1 file changed, 1 insertion(+) > > diff --git a/arch/riscv/kvm/vcpu_pmu.c b/arch/riscv/kvm/vcpu_pmu.c > index e873430e596b2..ac1b9a91a19db 100644 > --- a/arch/riscv/kvm/vcpu_pmu.c > +++ b/arch/riscv/kvm/vcpu_pmu.c > @@ -441,6 +441,7 @@ int kvm_riscv_vcpu_pmu_snapshot_set_shmem(struct kvm_vcpu *vcpu, unsigned long s > /* No need to check writable slot explicitly as kvm_vcpu_write_guest does it internally */ > if (kvm_vcpu_write_guest(vcpu, saddr, kvpmu->sdata, snapshot_area_size)) { > kfree(kvpmu->sdata); > + kvpmu->sdata = NULL; > sbiret = SBI_ERR_INVALID_ADDRESS; > goto out; > } > -- > 2.34.1 > Reviewed-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
On 3/18/2026 5:29 PM, Jiakai Xu wrote: > In kvm_riscv_vcpu_pmu_snapshot_set_shmem(), when kvm_vcpu_write_guest() > fails, kvpmu->sdata is freed but not set to NULL. This leaves a dangling > pointer that will be freed again when kvm_pmu_clear_snapshot_area() is > called during vcpu teardown, triggering a KASAN double-free report. > > First free occurs in kvm_riscv_vcpu_pmu_snapshot_set_shmem(): > kvm_riscv_vcpu_pmu_snapshot_set_shmem arch/riscv/kvm/vcpu_pmu.c:443 > kvm_sbi_ext_pmu_handler arch/riscv/kvm/vcpu_sbi_pmu.c:74 > kvm_riscv_vcpu_sbi_ecall arch/riscv/kvm/vcpu_sbi.c:608 > kvm_riscv_vcpu_exit arch/riscv/kvm/vcpu_exit.c:240 > kvm_arch_vcpu_ioctl_run arch/riscv/kvm/vcpu.c:1008 > kvm_vcpu_ioctl virt/kvm/kvm_main.c:4476 > > Second free (double-free) occurs in kvm_pmu_clear_snapshot_area(): > kvm_pmu_clear_snapshot_area arch/riscv/kvm/vcpu_pmu.c:403 [inline] > kvm_riscv_vcpu_pmu_deinit.part arch/riscv/kvm/vcpu_pmu.c:905 > kvm_riscv_vcpu_pmu_deinit arch/riscv/kvm/vcpu_pmu.c:893 > kvm_arch_vcpu_destroy arch/riscv/kvm/vcpu.c:199 > kvm_vcpu_destroy virt/kvm/kvm_main.c:469 [inline] > kvm_destroy_vcpus virt/kvm/kvm_main.c:489 > kvm_arch_destroy_vm arch/riscv/kvm/vm.c:54 > kvm_destroy_vm virt/kvm/kvm_main.c:1301 [inline] > kvm_put_kvm virt/kvm/kvm_main.c:1338 > kvm_vm_release virt/kvm/kvm_main.c:1361 > > Fix it by setting kvpmu->sdata to NULL after kfree() in > kvm_riscv_vcpu_pmu_snapshot_set_shmem(), so that the subsequent > kfree(NULL) in kvm_pmu_clear_snapshot_area() becomes a safe no-op. > > This bug was found by fuzzing the KVM RISC-V PMU interface. > > Fixes: c2f41ddbcdd756 ("RISC-V: KVM: Implement SBI PMU Snapshot feature") > Signed-off-by: Jiakai Xu <jiakaiPeanut@gmail.com> > Signed-off-by: Jiakai Xu <xujiakai2025@iscas.ac.cn> Reviewed-by: Nutty Liu <nutty.liu@hotmail.com> Thanks, Nutty > --- > arch/riscv/kvm/vcpu_pmu.c | 1 + > 1 file changed, 1 insertion(+) > > diff --git a/arch/riscv/kvm/vcpu_pmu.c b/arch/riscv/kvm/vcpu_pmu.c > index e873430e596b2..ac1b9a91a19db 100644 > --- a/arch/riscv/kvm/vcpu_pmu.c > +++ b/arch/riscv/kvm/vcpu_pmu.c > @@ -441,6 +441,7 @@ int kvm_riscv_vcpu_pmu_snapshot_set_shmem(struct kvm_vcpu *vcpu, unsigned long s > /* No need to check writable slot explicitly as kvm_vcpu_write_guest does it internally */ > if (kvm_vcpu_write_guest(vcpu, saddr, kvpmu->sdata, snapshot_area_size)) { > kfree(kvpmu->sdata); > + kvpmu->sdata = NULL; > sbiret = SBI_ERR_INVALID_ADDRESS; > goto out; > }
On Wed, Mar 18, 2026 at 3:00 PM Jiakai Xu <xujiakai2025@iscas.ac.cn> wrote: > > In kvm_riscv_vcpu_pmu_snapshot_set_shmem(), when kvm_vcpu_write_guest() > fails, kvpmu->sdata is freed but not set to NULL. This leaves a dangling > pointer that will be freed again when kvm_pmu_clear_snapshot_area() is > called during vcpu teardown, triggering a KASAN double-free report. > > First free occurs in kvm_riscv_vcpu_pmu_snapshot_set_shmem(): > kvm_riscv_vcpu_pmu_snapshot_set_shmem arch/riscv/kvm/vcpu_pmu.c:443 > kvm_sbi_ext_pmu_handler arch/riscv/kvm/vcpu_sbi_pmu.c:74 > kvm_riscv_vcpu_sbi_ecall arch/riscv/kvm/vcpu_sbi.c:608 > kvm_riscv_vcpu_exit arch/riscv/kvm/vcpu_exit.c:240 > kvm_arch_vcpu_ioctl_run arch/riscv/kvm/vcpu.c:1008 > kvm_vcpu_ioctl virt/kvm/kvm_main.c:4476 > > Second free (double-free) occurs in kvm_pmu_clear_snapshot_area(): > kvm_pmu_clear_snapshot_area arch/riscv/kvm/vcpu_pmu.c:403 [inline] > kvm_riscv_vcpu_pmu_deinit.part arch/riscv/kvm/vcpu_pmu.c:905 > kvm_riscv_vcpu_pmu_deinit arch/riscv/kvm/vcpu_pmu.c:893 > kvm_arch_vcpu_destroy arch/riscv/kvm/vcpu.c:199 > kvm_vcpu_destroy virt/kvm/kvm_main.c:469 [inline] > kvm_destroy_vcpus virt/kvm/kvm_main.c:489 > kvm_arch_destroy_vm arch/riscv/kvm/vm.c:54 > kvm_destroy_vm virt/kvm/kvm_main.c:1301 [inline] > kvm_put_kvm virt/kvm/kvm_main.c:1338 > kvm_vm_release virt/kvm/kvm_main.c:1361 > > Fix it by setting kvpmu->sdata to NULL after kfree() in > kvm_riscv_vcpu_pmu_snapshot_set_shmem(), so that the subsequent > kfree(NULL) in kvm_pmu_clear_snapshot_area() becomes a safe no-op. > > This bug was found by fuzzing the KVM RISC-V PMU interface. > > Fixes: c2f41ddbcdd756 ("RISC-V: KVM: Implement SBI PMU Snapshot feature") > Signed-off-by: Jiakai Xu <jiakaiPeanut@gmail.com> > Signed-off-by: Jiakai Xu <xujiakai2025@iscas.ac.cn> Queued this patch for Linux-7.1 Thanks, Anup > --- > arch/riscv/kvm/vcpu_pmu.c | 1 + > 1 file changed, 1 insertion(+) > > diff --git a/arch/riscv/kvm/vcpu_pmu.c b/arch/riscv/kvm/vcpu_pmu.c > index e873430e596b2..ac1b9a91a19db 100644 > --- a/arch/riscv/kvm/vcpu_pmu.c > +++ b/arch/riscv/kvm/vcpu_pmu.c > @@ -441,6 +441,7 @@ int kvm_riscv_vcpu_pmu_snapshot_set_shmem(struct kvm_vcpu *vcpu, unsigned long s > /* No need to check writable slot explicitly as kvm_vcpu_write_guest does it internally */ > if (kvm_vcpu_write_guest(vcpu, saddr, kvpmu->sdata, snapshot_area_size)) { > kfree(kvpmu->sdata); > + kvpmu->sdata = NULL; > sbiret = SBI_ERR_INVALID_ADDRESS; > goto out; > } > -- > 2.34.1 >
diff --git a/arch/riscv/kvm/vcpu_pmu.c b/arch/riscv/kvm/vcpu_pmu.c index e873430e596b2..ac1b9a91a19db 100644 --- a/arch/riscv/kvm/vcpu_pmu.c +++ b/arch/riscv/kvm/vcpu_pmu.c @@ -441,6 +441,7 @@ int kvm_riscv_vcpu_pmu_snapshot_set_shmem(struct kvm_vcpu *vcpu, unsigned long s /* No need to check writable slot explicitly as kvm_vcpu_write_guest does it internally */ if (kvm_vcpu_write_guest(vcpu, saddr, kvpmu->sdata, snapshot_area_size)) { kfree(kvpmu->sdata); + kvpmu->sdata = NULL; sbiret = SBI_ERR_INVALID_ADDRESS; goto out; }
In kvm_riscv_vcpu_pmu_snapshot_set_shmem(), when kvm_vcpu_write_guest() fails, kvpmu->sdata is freed but not set to NULL. This leaves a dangling pointer that will be freed again when kvm_pmu_clear_snapshot_area() is called during vcpu teardown, triggering a KASAN double-free report. First free occurs in kvm_riscv_vcpu_pmu_snapshot_set_shmem(): kvm_riscv_vcpu_pmu_snapshot_set_shmem arch/riscv/kvm/vcpu_pmu.c:443 kvm_sbi_ext_pmu_handler arch/riscv/kvm/vcpu_sbi_pmu.c:74 kvm_riscv_vcpu_sbi_ecall arch/riscv/kvm/vcpu_sbi.c:608 kvm_riscv_vcpu_exit arch/riscv/kvm/vcpu_exit.c:240 kvm_arch_vcpu_ioctl_run arch/riscv/kvm/vcpu.c:1008 kvm_vcpu_ioctl virt/kvm/kvm_main.c:4476 Second free (double-free) occurs in kvm_pmu_clear_snapshot_area(): kvm_pmu_clear_snapshot_area arch/riscv/kvm/vcpu_pmu.c:403 [inline] kvm_riscv_vcpu_pmu_deinit.part arch/riscv/kvm/vcpu_pmu.c:905 kvm_riscv_vcpu_pmu_deinit arch/riscv/kvm/vcpu_pmu.c:893 kvm_arch_vcpu_destroy arch/riscv/kvm/vcpu.c:199 kvm_vcpu_destroy virt/kvm/kvm_main.c:469 [inline] kvm_destroy_vcpus virt/kvm/kvm_main.c:489 kvm_arch_destroy_vm arch/riscv/kvm/vm.c:54 kvm_destroy_vm virt/kvm/kvm_main.c:1301 [inline] kvm_put_kvm virt/kvm/kvm_main.c:1338 kvm_vm_release virt/kvm/kvm_main.c:1361 Fix it by setting kvpmu->sdata to NULL after kfree() in kvm_riscv_vcpu_pmu_snapshot_set_shmem(), so that the subsequent kfree(NULL) in kvm_pmu_clear_snapshot_area() becomes a safe no-op. This bug was found by fuzzing the KVM RISC-V PMU interface. Fixes: c2f41ddbcdd756 ("RISC-V: KVM: Implement SBI PMU Snapshot feature") Signed-off-by: Jiakai Xu <jiakaiPeanut@gmail.com> Signed-off-by: Jiakai Xu <xujiakai2025@iscas.ac.cn> --- arch/riscv/kvm/vcpu_pmu.c | 1 + 1 file changed, 1 insertion(+)