]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: arm64: Prevent teardown finalisation of referenced 'hyp_vm'
authorWill Deacon <will@kernel.org>
Tue, 31 Mar 2026 15:50:53 +0000 (16:50 +0100)
committerMarc Zyngier <maz@kernel.org>
Wed, 1 Apr 2026 13:29:06 +0000 (14:29 +0100)
Destroying a 'hyp_vm' with an elevated referenced count in
__pkvm_finalize_teardown_vm() is only going to lead to tears.

In preparation for allowing limited references to be acquired on dying
VMs during the teardown process, factor out the handle-to-vm logic for
the teardown path and reuse it for both the 'start' and 'finalise'
stages of the teardown process.

Signed-off-by: Will Deacon <will@kernel.org>
Link: https://patch.msgid.link/20260331155056.28220-2-will@kernel.org
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/kvm/hyp/nvhe/pkvm.c

index 8b906217c4c3532cdeec53a2d13e428339ebb505..3fd3b930beeb81c6fb0992fa619d03f94b537cfa 100644 (file)
@@ -936,20 +936,27 @@ int __pkvm_reclaim_dying_guest_page(pkvm_handle_t handle, u64 gfn)
        return ret;
 }
 
+static struct pkvm_hyp_vm *get_pkvm_unref_hyp_vm_locked(pkvm_handle_t handle)
+{
+       struct pkvm_hyp_vm *hyp_vm;
+
+       hyp_assert_lock_held(&vm_table_lock);
+
+       hyp_vm = get_vm_by_handle(handle);
+       if (!hyp_vm || hyp_page_count(hyp_vm))
+               return NULL;
+
+       return hyp_vm;
+}
+
 int __pkvm_start_teardown_vm(pkvm_handle_t handle)
 {
        struct pkvm_hyp_vm *hyp_vm;
        int ret = 0;
 
        hyp_spin_lock(&vm_table_lock);
-       hyp_vm = get_vm_by_handle(handle);
-       if (!hyp_vm) {
-               ret = -ENOENT;
-               goto unlock;
-       } else if (WARN_ON(hyp_page_count(hyp_vm))) {
-               ret = -EBUSY;
-               goto unlock;
-       } else if (hyp_vm->kvm.arch.pkvm.is_dying) {
+       hyp_vm = get_pkvm_unref_hyp_vm_locked(handle);
+       if (!hyp_vm || hyp_vm->kvm.arch.pkvm.is_dying) {
                ret = -EINVAL;
                goto unlock;
        }
@@ -971,12 +978,9 @@ int __pkvm_finalize_teardown_vm(pkvm_handle_t handle)
        int err;
 
        hyp_spin_lock(&vm_table_lock);
-       hyp_vm = get_vm_by_handle(handle);
-       if (!hyp_vm) {
-               err = -ENOENT;
-               goto err_unlock;
-       } else if (!hyp_vm->kvm.arch.pkvm.is_dying) {
-               err = -EBUSY;
+       hyp_vm = get_pkvm_unref_hyp_vm_locked(handle);
+       if (!hyp_vm || !hyp_vm->kvm.arch.pkvm.is_dying) {
+               err = -EINVAL;
                goto err_unlock;
        }