KVM: x86: Allow userspace to opt out of hypercall patching
authorOliver Upton <oupton@google.com>
Wed, 16 Mar 2022 00:55:37 +0000 (00:55 +0000)
committerPaolo Bonzini <pbonzini@redhat.com>
Sat, 2 Apr 2022 09:41:10 +0000 (05:41 -0400)
KVM handles the VMCALL/VMMCALL instructions very strangely. Even though
both of these instructions really should #UD when executed on the wrong
vendor's hardware (i.e. VMCALL on SVM, VMMCALL on VMX), KVM replaces the
guest's instruction with the appropriate instruction for the vendor.
Nonetheless, older guest kernels without commit c1118b3602c2 ("x86: kvm:
use alternatives for VMCALL vs. VMMCALL if kernel text is read-only")
do not patch in the appropriate instruction using alternatives, likely
motivating KVM's intervention.

Add a quirk allowing userspace to opt out of hypercall patching. If the
quirk is disabled, KVM synthesizes a #UD in the guest.

Signed-off-by: Oliver Upton <oupton@google.com>
Message-Id: <20220316005538.2282772-2-oupton@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Documentation/virt/kvm/api.rst
arch/x86/include/asm/kvm_host.h
arch/x86/include/uapi/asm/kvm.h
arch/x86/kvm/x86.c

index d13fa66004672c9226b91f9b91e5b048af2943c2..700df350332b1166e9986e0dd9b3dc5235c92f44 100644 (file)
@@ -7134,6 +7134,15 @@ The valid bits in cap.args[0] are:
                                     Additionally, when this quirk is disabled,
                                     KVM clears CPUID.01H:ECX[bit 3] if
                                     IA32_MISC_ENABLE[bit 18] is cleared.
+
+ KVM_X86_QUIRK_FIX_HYPERCALL_INSN   By default, KVM rewrites guest
+                                    VMMCALL/VMCALL instructions to match the
+                                    vendor's hypercall instruction for the
+                                    system. When this quirk is disabled, KVM
+                                    will no longer rewrite invalid guest
+                                    hypercall instructions. Executing the
+                                    incorrect hypercall instruction will
+                                    generate a #UD within the guest.
 =================================== ============================================
 
 8. Other capabilities.
index 93a2671a57cf2dc7354729c6c3b6687d1722b368..d9a825182b845d82ef4830fe96ebbda8ce5ae460 100644 (file)
@@ -1987,6 +1987,7 @@ int memslot_rmap_alloc(struct kvm_memory_slot *slot, unsigned long npages);
         KVM_X86_QUIRK_CD_NW_CLEARED |          \
         KVM_X86_QUIRK_LAPIC_MMIO_HOLE |        \
         KVM_X86_QUIRK_OUT_7E_INC_RIP |         \
-        KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT)
+        KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT |   \
+        KVM_X86_QUIRK_FIX_HYPERCALL_INSN)
 
 #endif /* _ASM_X86_KVM_HOST_H */
index bf6e96011dfed4e2e8582e511c08b5b6cbd723b6..21614807a2cbb35912c169abe76e9ef649d0ae5b 100644 (file)
@@ -428,11 +428,12 @@ struct kvm_sync_regs {
        struct kvm_vcpu_events events;
 };
 
-#define KVM_X86_QUIRK_LINT0_REENABLED     (1 << 0)
-#define KVM_X86_QUIRK_CD_NW_CLEARED       (1 << 1)
-#define KVM_X86_QUIRK_LAPIC_MMIO_HOLE     (1 << 2)
-#define KVM_X86_QUIRK_OUT_7E_INC_RIP      (1 << 3)
-#define KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT (1 << 4)
+#define KVM_X86_QUIRK_LINT0_REENABLED          (1 << 0)
+#define KVM_X86_QUIRK_CD_NW_CLEARED            (1 << 1)
+#define KVM_X86_QUIRK_LAPIC_MMIO_HOLE          (1 << 2)
+#define KVM_X86_QUIRK_OUT_7E_INC_RIP           (1 << 3)
+#define KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT     (1 << 4)
+#define KVM_X86_QUIRK_FIX_HYPERCALL_INSN       (1 << 5)
 
 #define KVM_STATE_NESTED_FORMAT_VMX    0
 #define KVM_STATE_NESTED_FORMAT_SVM    1
index 498cd4aaa2cb362915439441bd89915a43db50a6..c9964b78fa2e211e0b7c287c7a949bb45b841f5f 100644 (file)
@@ -9267,6 +9267,17 @@ static int emulator_fix_hypercall(struct x86_emulate_ctxt *ctxt)
        char instruction[3];
        unsigned long rip = kvm_rip_read(vcpu);
 
+       /*
+        * If the quirk is disabled, synthesize a #UD and let the guest pick up
+        * the pieces.
+        */
+       if (!kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_FIX_HYPERCALL_INSN)) {
+               ctxt->exception.error_code_valid = false;
+               ctxt->exception.vector = UD_VECTOR;
+               ctxt->have_exception = true;
+               return X86EMUL_PROPAGATE_FAULT;
+       }
+
        static_call(kvm_x86_patch_hypercall)(vcpu, instruction);
 
        return emulator_write_emulated(ctxt, rip, instruction, 3,