| From eab115c8a25939f44491de7f366af4ff07f6feb1 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 25 Feb 2021 12:47:30 -0800 |
| Subject: KVM: x86/mmu: Retry page faults that hit an invalid memslot |
| |
| From: Sean Christopherson <seanjc@google.com> |
| |
| [ Upstream commit e0c378684b6545ad2d4403bb701d0ac4932b4e95 ] |
| |
| Retry page faults (re-enter the guest) that hit an invalid memslot |
| instead of treating the memslot as not existing, i.e. handling the |
| page fault as an MMIO access. When deleting a memslot, SPTEs aren't |
| zapped and the TLBs aren't flushed until after the memslot has been |
| marked invalid. |
| |
| Handling the invalid slot as MMIO means there's a small window where a |
| page fault could replace a valid SPTE with an MMIO SPTE. The legacy |
| MMU handles such a scenario cleanly, but the TDP MMU assumes such |
| behavior is impossible (see the BUG() in __handle_changed_spte()). |
| There's really no good reason why the legacy MMU should allow such a |
| scenario, and closing this hole allows for additional cleanups. |
| |
| Fixes: 2f2fad0897cb ("kvm: x86/mmu: Add functions to handle changed TDP SPTEs") |
| Cc: Ben Gardon <bgardon@google.com> |
| Signed-off-by: Sean Christopherson <seanjc@google.com> |
| Message-Id: <20210225204749.1512652-6-seanjc@google.com> |
| Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| arch/x86/kvm/mmu/mmu.c | 8 ++++++++ |
| 1 file changed, 8 insertions(+) |
| |
| diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c |
| index 3d5e4fdbf5fd..9dabd689a812 100644 |
| --- a/arch/x86/kvm/mmu/mmu.c |
| +++ b/arch/x86/kvm/mmu/mmu.c |
| @@ -3676,6 +3676,14 @@ static bool try_async_pf(struct kvm_vcpu *vcpu, bool prefault, gfn_t gfn, |
| struct kvm_memory_slot *slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn); |
| bool async; |
| |
| + /* |
| + * Retry the page fault if the gfn hit a memslot that is being deleted |
| + * or moved. This ensures any existing SPTEs for the old memslot will |
| + * be zapped before KVM inserts a new MMIO SPTE for the gfn. |
| + */ |
| + if (slot && (slot->flags & KVM_MEMSLOT_INVALID)) |
| + return true; |
| + |
| /* Don't expose private memslots to L2. */ |
| if (is_guest_mode(vcpu) && !kvm_is_visible_memslot(slot)) { |
| *pfn = KVM_PFN_NOSLOT; |
| -- |
| 2.30.2 |
| |