KVM: VMX: exit to userspace on exception w/ invalid PM
diff mbox

Message ID 20180323163400.2226-1-sean.j.christopherson@intel.com
State New
Headers show

Commit Message

Sean Christopherson March 23, 2018, 4:34 p.m. UTC
Exit to userspace with KVM_INTERNAL_ERROR_EMULATION if we encounter
an exception in Protected Mode while emulating guest due to invalid
guest state.  Unlike Big RM, KVM doesn't support emulating exceptions
in PM, i.e. PM exceptions are always injected via the VMCS.  Because
we will never do VMRESUME due to emulation_required, the exception is
never realized and we'll keep emulating the faulting instruction over
and over until we receive a signal.

Exit to userspace iff there is a pending exception, i.e. don't exit
simply on a requested event. The purpose of this check and exit is to
aid in debugging a guest that is in all likelihood already doomed.
Invalid guest state in PM is extremely limited in normal operation,
e.g. it generally only occurs for a few instructions early in BIOS,
and any exception at this time is all but guaranteed to be fatal.
Non-vectored interrupts, e.g. INIT, SIPI and SMI, can be cleanly
handled/emulated, while checking for vectored interrupts, e.g. INTR
and NMI, without hitting false positives would add a fair amount of
complexity for almost no benefit (getting hit by lightning seems
more likely than encountering this specific scenario).

Add a WARN_ON_ONCE to vmx_queue_exception() if we try to inject an
exception via the VMCS and emulation_required is true.

Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
---
 arch/x86/kvm/vmx.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

Comments

Radim Krčmář March 29, 2018, 9:42 p.m. UTC | #1
2018-03-23 09:34-0700, Sean Christopherson:
> Exit to userspace with KVM_INTERNAL_ERROR_EMULATION if we encounter
> an exception in Protected Mode while emulating guest due to invalid
> guest state.  Unlike Big RM, KVM doesn't support emulating exceptions
> in PM, i.e. PM exceptions are always injected via the VMCS.  Because
> we will never do VMRESUME due to emulation_required, the exception is
> never realized and we'll keep emulating the faulting instruction over
> and over until we receive a signal.
> 
> Exit to userspace iff there is a pending exception, i.e. don't exit
> simply on a requested event. The purpose of this check and exit is to
> aid in debugging a guest that is in all likelihood already doomed.
> Invalid guest state in PM is extremely limited in normal operation,
> e.g. it generally only occurs for a few instructions early in BIOS,
> and any exception at this time is all but guaranteed to be fatal.
> Non-vectored interrupts, e.g. INIT, SIPI and SMI, can be cleanly
> handled/emulated, while checking for vectored interrupts, e.g. INTR
> and NMI, without hitting false positives would add a fair amount of
> complexity for almost no benefit (getting hit by lightning seems
> more likely than encountering this specific scenario).
> 
> Add a WARN_ON_ONCE to vmx_queue_exception() if we try to inject an
> exception via the VMCS and emulation_required is true.
> 
> Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
> ---

Queued, thanks.

Patch
diff mbox

diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index b4d8da6c62c8..983123b8cfa4 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -2578,6 +2578,8 @@  static void vmx_queue_exception(struct kvm_vcpu *vcpu)
 		return;
 	}
 
+	WARN_ON_ONCE(vmx->emulation_required);
+
 	if (kvm_exception_is_soft(nr)) {
 		vmcs_write32(VM_ENTRY_INSTRUCTION_LEN,
 			     vmx->vcpu.arch.event_exit_inst_len);
@@ -6908,12 +6910,12 @@  static int handle_invalid_guest_state(struct kvm_vcpu *vcpu)
 			goto out;
 		}
 
-		if (err != EMULATE_DONE) {
-			vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
-			vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
-			vcpu->run->internal.ndata = 0;
-			return 0;
-		}
+		if (err != EMULATE_DONE)
+			goto emulation_error;
+
+		if (vmx->emulation_required && !vmx->rmode.vm86_active &&
+		    vcpu->arch.exception.pending)
+			goto emulation_error;
 
 		if (vcpu->arch.halt_request) {
 			vcpu->arch.halt_request = 0;
@@ -6929,6 +6931,12 @@  static int handle_invalid_guest_state(struct kvm_vcpu *vcpu)
 
 out:
 	return ret;
+
+emulation_error:
+	vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
+	vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
+	vcpu->run->internal.ndata = 0;
+	return 0;
 }
 
 static int __grow_ple_window(int val)