ANDROID: KVM: arm64: Disallow kvm_pgtable_stage2_unmap on host S2 The TLBI logic is unsafe for the host stage-2 table because not all valid ptes are reference counted. Hence we could discard a child table with only a __kvm_tlb_flush_vmid_ipa which is insufficient to invalidate all mappings still reachable from the child table. Bug: 278749606 Bug: 360102354 Change-Id: I0080b357743b335a4915327456f7ffc1dc2b7069 Signed-off-by: Keir Fraser <keirf@google.com>
diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c index b11bceb..460263d4 100644 --- a/arch/arm64/kvm/hyp/pgtable.c +++ b/arch/arm64/kvm/hyp/pgtable.c
@@ -1157,6 +1157,18 @@ int kvm_pgtable_stage2_unmap(struct kvm_pgtable *pgt, u64 addr, u64 size) .flags = KVM_PGTABLE_WALK_LEAF | KVM_PGTABLE_WALK_TABLE_POST, }; + /* + * stage2_unmap_walker's TLBI logic is unsafe for the pKVM host stage-2 + * table because a child table may have a refcount of 1 while still + * containing valid mappings. The use of __kvm_tlb_flush_vmid_ipa in + * stage2_unmap_clear_pte is then insufficient to invalidate all leaf + * mappings reachable from the child table. All other stage-2 tables + * hold a reference for every non-zero PTE, and are thus guaranteed to + * be completely empty when refcount is 1. + */ + if (WARN_ON(pgt->flags & KVM_PGTABLE_S2_IDMAP)) + return -EINVAL; + ret = kvm_pgtable_walk(pgt, addr, size, &walker); if (stage2_unmap_defer_tlb_flush(pgt)) /* Perform the deferred TLB invalidations */