Skip to content

Commit b74d002

Browse files
mdrothbonzini
authored andcommitted
KVM: MMU: Disable fast path if KVM_EXIT_MEMORY_FAULT is needed
For hardware-protected VMs like SEV-SNP guests, certain conditions like attempting to perform a write to a page which is not in the state that the guest expects it to be in can result in a nested/extended #PF which can only be satisfied by the host performing an implicit page state change to transition the page into the expected shared/private state. This is generally handled by generating a KVM_EXIT_MEMORY_FAULT event that gets forwarded to userspace to handle via KVM_SET_MEMORY_ATTRIBUTES. However, the fast_page_fault() code might misconstrue this situation as being the result of a write-protected access, and treat it as a spurious case when it sees that writes are already allowed for the sPTE. This results in the KVM MMU trying to resume the guest rather than taking any action to satisfy the real source of the #PF such as generating a KVM_EXIT_MEMORY_FAULT, resulting in the guest spinning on nested #PFs. Check for this condition and bail out of the fast path if it is detected. Suggested-by: Paolo Bonzini <pbonzini@redhat.com> Suggested-by: Sean Christopherson <seanjc@google.com> Cc: Isaku Yamahata <isaku.yamahata@intel.com> Reviewed-by: Isaku Yamahata <isaku.yamahata@intel.com> Signed-off-by: Michael Roth <michael.roth@amd.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent 7323260 commit b74d002

1 file changed

Lines changed: 22 additions & 2 deletions

File tree

arch/x86/kvm/mmu/mmu.c

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3305,7 +3305,7 @@ static int kvm_handle_noslot_fault(struct kvm_vcpu *vcpu,
33053305
return RET_PF_CONTINUE;
33063306
}
33073307

3308-
static bool page_fault_can_be_fast(struct kvm_page_fault *fault)
3308+
static bool page_fault_can_be_fast(struct kvm *kvm, struct kvm_page_fault *fault)
33093309
{
33103310
/*
33113311
* Page faults with reserved bits set, i.e. faults on MMIO SPTEs, only
@@ -3316,6 +3316,26 @@ static bool page_fault_can_be_fast(struct kvm_page_fault *fault)
33163316
if (fault->rsvd)
33173317
return false;
33183318

3319+
/*
3320+
* For hardware-protected VMs, certain conditions like attempting to
3321+
* perform a write to a page which is not in the state that the guest
3322+
* expects it to be in can result in a nested/extended #PF. In this
3323+
* case, the below code might misconstrue this situation as being the
3324+
* result of a write-protected access, and treat it as a spurious case
3325+
* rather than taking any action to satisfy the real source of the #PF
3326+
* such as generating a KVM_EXIT_MEMORY_FAULT. This can lead to the
3327+
* guest spinning on a #PF indefinitely, so don't attempt the fast path
3328+
* in this case.
3329+
*
3330+
* Note that the kvm_mem_is_private() check might race with an
3331+
* attribute update, but this will either result in the guest spinning
3332+
* on RET_PF_SPURIOUS until the update completes, or an actual spurious
3333+
* case might go down the slow path. Either case will resolve itself.
3334+
*/
3335+
if (kvm->arch.has_private_mem &&
3336+
fault->is_private != kvm_mem_is_private(kvm, fault->gfn))
3337+
return false;
3338+
33193339
/*
33203340
* #PF can be fast if:
33213341
*
@@ -3416,7 +3436,7 @@ static int fast_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault)
34163436
u64 *sptep;
34173437
uint retry_count = 0;
34183438

3419-
if (!page_fault_can_be_fast(fault))
3439+
if (!page_fault_can_be_fast(vcpu->kvm, fault))
34203440
return ret;
34213441

34223442
walk_shadow_page_lockless_begin(vcpu);

0 commit comments

Comments
 (0)