]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: arm64: Fix __pkvm_init_vm error path
authorVincent Donnefort <vdonnefort@google.com>
Thu, 21 May 2026 14:36:25 +0000 (15:36 +0100)
committerMarc Zyngier <maz@kernel.org>
Tue, 26 May 2026 15:12:29 +0000 (16:12 +0100)
In the unlikely case where insert_vm_table_entry fails, __pkvm_init_vm
release the memory donated by the host for the PGD, but as the stage-2
is still set-up the hypervisor keeps a refcount on those pages,
effectively leaking the references.

Fix the rollback with the newly added kvm_guest_destroy_stage2().

Fixes: 256b4668cd89 ("KVM: arm64: Introduce separate hypercalls for pKVM VM reservation and initialization")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Reviewed-by: Fuad Tabba <tabba@google.com>
Tested-by: Fuad Tabba <tabba@google.com>
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
Link: https://patch.msgid.link/20260521143626.1005660-3-vdonnefort@google.com
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
arch/arm64/kvm/hyp/nvhe/mem_protect.c
arch/arm64/kvm/hyp/nvhe/pkvm.c

index 3cbfae0e3dda13c5f802629de5545b0d4a6f37a7..4f2b871199cb4138f5867179a1abf00d92efd40f 100644 (file)
@@ -56,6 +56,7 @@ int host_stage2_idmap_locked(phys_addr_t addr, u64 size, enum kvm_pgtable_prot p
 int host_stage2_set_owner_locked(phys_addr_t addr, u64 size, u8 owner_id);
 int kvm_host_prepare_stage2(void *pgt_pool_base);
 int kvm_guest_prepare_stage2(struct pkvm_hyp_vm *vm, void *pgd);
+void kvm_guest_destroy_stage2(struct pkvm_hyp_vm *vm);
 void handle_host_mem_abort(struct kvm_cpu_context *host_ctxt);
 
 int hyp_pin_shared_mem(void *from, void *to);
index 5c1e1742db4f0d81c7ee9f8259fbcf285b4e4cae..88cd723322085b7892351138c98fd34d2218e651 100644 (file)
@@ -290,16 +290,21 @@ int kvm_guest_prepare_stage2(struct pkvm_hyp_vm *vm, void *pgd)
        return 0;
 }
 
+void kvm_guest_destroy_stage2(struct pkvm_hyp_vm *vm)
+{
+       guest_lock_component(vm);
+       kvm_pgtable_stage2_destroy(&vm->pgt);
+       vm->kvm.arch.mmu.pgd_phys = 0ULL;
+       guest_unlock_component(vm);
+}
+
 void reclaim_pgtable_pages(struct pkvm_hyp_vm *vm, struct kvm_hyp_memcache *mc)
 {
        struct hyp_page *page;
        void *addr;
 
        /* Dump all pgtable pages in the hyp_pool */
-       guest_lock_component(vm);
-       kvm_pgtable_stage2_destroy(&vm->pgt);
-       vm->kvm.arch.mmu.pgd_phys = 0ULL;
-       guest_unlock_component(vm);
+       kvm_guest_destroy_stage2(vm);
 
        /* Drain the hyp_pool into the memcache */
        addr = hyp_alloc_pages(&vm->pool, 0);
index e7496eb8562897cea1f21ccb77ebcde2abaf9eba..ff89b30f5c4a08a17dbe321c6473b553ab351116 100644 (file)
@@ -839,10 +839,12 @@ int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva,
        /* Must be called last since this publishes the VM. */
        ret = insert_vm_table_entry(handle, hyp_vm);
        if (ret)
-               goto err_remove_mappings;
+               goto err_destroy_stage2;
 
        return 0;
 
+err_destroy_stage2:
+       kvm_guest_destroy_stage2(hyp_vm);
 err_remove_mappings:
        unmap_donated_memory(hyp_vm, vm_size);
        unmap_donated_memory(pgd, pgd_size);