KVM: x86/mmu: Exit to userspace with -EFAULT if private fault hits emulation
authorSean Christopherson <seanjc@google.com>
Wed, 28 Feb 2024 02:41:32 +0000 (18:41 -0800)
committerPaolo Bonzini <pbonzini@redhat.com>
Tue, 7 May 2024 15:59:16 +0000 (11:59 -0400)
Exit to userspace with -EFAULT / KVM_EXIT_MEMORY_FAULT if a private fault
triggers emulation of any kind, as KVM doesn't currently support emulating
access to guest private memory.  Practically speaking, private faults and
emulation are already mutually exclusive, but there are many flow that
can result in KVM returning RET_PF_EMULATE, and adding one last check
to harden against weird, unexpected combinations and/or KVM bugs is
inexpensive.

Suggested-by: Yan Zhao <yan.y.zhao@intel.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-ID: <20240228024147.41573-2-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/kvm/mmu/mmu.c
arch/x86/kvm/mmu/mmu_internal.h

index 08900a0563f9bd1f172cbf3d6695867f592e9912..955de52e0c182907ac670b95d32ab3966f093b50 100644 (file)
@@ -4256,14 +4256,6 @@ static inline u8 kvm_max_level_for_order(int order)
        return PG_LEVEL_4K;
 }
 
-static void kvm_mmu_prepare_memory_fault_exit(struct kvm_vcpu *vcpu,
-                                             struct kvm_page_fault *fault)
-{
-       kvm_prepare_memory_fault_exit(vcpu, fault->gfn << PAGE_SHIFT,
-                                     PAGE_SIZE, fault->write, fault->exec,
-                                     fault->is_private);
-}
-
 static int kvm_faultin_pfn_private(struct kvm_vcpu *vcpu,
                                   struct kvm_page_fault *fault)
 {
index 5390a591a5718cce422958210e3eb91f457bd169..61f49967047ad1a3e2b0f605963f2546441bdbd4 100644 (file)
@@ -279,6 +279,14 @@ enum {
        RET_PF_SPURIOUS,
 };
 
+static inline void kvm_mmu_prepare_memory_fault_exit(struct kvm_vcpu *vcpu,
+                                                    struct kvm_page_fault *fault)
+{
+       kvm_prepare_memory_fault_exit(vcpu, fault->gfn << PAGE_SHIFT,
+                                     PAGE_SIZE, fault->write, fault->exec,
+                                     fault->is_private);
+}
+
 static inline int kvm_mmu_do_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
                                        u32 err, bool prefetch, int *emulation_type)
 {
@@ -320,6 +328,17 @@ static inline int kvm_mmu_do_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
        else
                r = vcpu->arch.mmu->page_fault(vcpu, &fault);
 
+       /*
+        * Not sure what's happening, but punt to userspace and hope that
+        * they can fix it by changing memory to shared, or they can
+        * provide a better error.
+        */
+       if (r == RET_PF_EMULATE && fault.is_private) {
+               pr_warn_ratelimited("kvm: unexpected emulation request on private memory\n");
+               kvm_mmu_prepare_memory_fault_exit(vcpu, &fault);
+               return -EFAULT;
+       }
+
        if (fault.write_fault_to_shadow_pgtable && emulation_type)
                *emulation_type |= EMULTYPE_WRITE_PF_TO_SP;