[PATCH v2 3/3] KVM: arm64: Roll back partial shares on kvm_share_hyp() failure

From: tabba

Date: Fri May 29 2026 - 08:26:01 EST


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@xxxxxxxxxx>
Signed-off-by: Fuad Tabba <tabba@xxxxxxxxxx>
---
arch/arm64/kvm/mmu.c | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index e08503e89fc4..8811ad60cf72 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -544,8 +544,8 @@ static int unshare_pfn_hyp(u64 pfn)
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)
--
2.54.0.929.g9b7fa37559-goog