]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: arm64: Roll back partial shares on kvm_share_hyp() failure
authortabba@google.com <tabba@google.com>
Fri, 29 May 2026 12:17:55 +0000 (13:17 +0100)
committerMarc Zyngier <maz@kernel.org>
Sun, 7 Jun 2026 13:38:56 +0000 (14:38 +0100)
kvm_share_hyp() shares a range one page at a time. If share_pfn_hyp()
fails partway through, the pages already shared by this call are left
shared, while the caller treats the whole range as failed and never
unshares them.

Unshare those pages before returning the error. If an unshare itself
fails the page is leaked: it stays shared with the hypervisor and is
no longer reusable for pKVM, but no isolation guarantee is broken, so
WARN and continue. Not expected in practice.

Fixes: a83e2191b7f1 ("KVM: arm64: pkvm: Refcount the pages shared with EL2")
Suggested-by: Vincent Donnefort <vdonnefort@google.com>
Signed-off-by: Fuad Tabba <tabba@google.com>
Reviewed-by: Vincent Donnefort <vdonnefort@google.com>
Link: https://patch.msgid.link/20260529121755.2923500-4-tabba@google.com
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/kvm/mmu.c

index c82d4ececab8d3eb3e3b2c39ec806bae78da1f76..f18b287c98b62b9116b4ad5b00851939ae79704a 100644 (file)
@@ -544,8 +544,8 @@ unlock:
 int kvm_share_hyp(void *from, void *to)
 {
        phys_addr_t start, end, cur;
+       int ret = 0;
        u64 pfn;
-       int ret;
 
        if (is_kernel_in_hyp_mode())
                return 0;
@@ -567,10 +567,24 @@ int kvm_share_hyp(void *from, void *to)
                pfn = __phys_to_pfn(cur);
                ret = share_pfn_hyp(pfn);
                if (ret)
-                       return ret;
+                       break;
        }
 
-       return 0;
+       if (!ret)
+               return 0;
+
+       /*
+        * Roll back the pages shared by this call. A failed unshare leaks
+        * the page (it stays shared with the hypervisor and is no longer
+        * reusable for pKVM) but breaks no isolation guarantee, so warn and
+        * continue. Not expected in practice.
+        */
+       for (end = cur, cur = start; cur < end; cur += PAGE_SIZE) {
+               pfn = __phys_to_pfn(cur);
+               WARN_ON(unshare_pfn_hyp(pfn));
+       }
+
+       return ret;
 }
 
 void kvm_unshare_hyp(void *from, void *to)