Subject: x86/vtx: Work around SingleStep + STI/MovSS VMEntry failures
From: Andrew Cooper <[email-masked]>

See patch comment for technical details.

Concerning the timeline, this was first discovered in the aftermath of
XSA-156 which caused #DB to be intercepted unconditionally, but only in
its SingleStep + STI form which is restricted to privileged software.

After working with Intel and identifying the problematic vmentry check,
this workaround was suggested, and the patch was posted in an RFC
series.  Outstanding work for that series (not breaking Introspection)
is still pending, and this fix from it (which wouldn't have been good
enough in its original form) wasn't committed.

A vmentry failure was reported to xen-devel, and debugging identified
this bug in its SingleStep + MovSS form by way of INT1, which does not
involve the use of any privileged instructions, and proving this to be a
security issue.

This is XSA-308

Reported-by: Håkon Alstadheim <[email-masked]>
Signed-off-by: Andrew Cooper <[email-masked]>
Reviewed-by: Jan Beulich <[email-masked]>
Acked-by: Kevin Tian <[email-masked]>

diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c
index 6a5eeb5c13..59b836f43f 100644
--- a/xen/arch/x86/hvm/vmx/vmx.c
+++ b/xen/arch/x86/hvm/vmx/vmx.c
@@ -3816,6 +3816,42 @@ void vmx_vmexit_handler(struct cpu_user_regs *regs)
             HVMTRACE_1D(TRAP_DEBUG, exit_qualification);
             __restore_debug_registers(v);
             write_debugreg(6, exit_qualification | DR_STATUS_RESERVED_ONE);
+
+            /*
+             * Work around SingleStep + STI/MovSS VMEntry failures.
+             *
+             * We intercept #DB unconditionally to work around CVE-2015-8104 /
+             * XSA-156 (guest-kernel induced host DoS).
+             *
+             * STI/MovSS shadows block/defer interrupts/exceptions (exact
+             * details are complicated and poorly documented).  Debug
+             * exceptions delayed for any reason are stored in the
+             * PENDING_DBG_EXCEPTIONS field.
+             *
+             * The falling edge of PENDING_DBG causes #DB to be delivered,
+             * resulting in a VMExit, as #DB is intercepted.  The VMCS still
+             * reports blocked-by-STI/MovSS.
+             *
+             * The VMEntry checks when EFLAGS.TF is set don't like a VMCS in
+             * this state.  Despite a #DB queued in VMENTRY_INTR_INFO, the
+             * state is rejected as DR6.BS isn't pending.  Fix this up.
+             */
+            if ( unlikely(regs->eflags & X86_EFLAGS_TF) )
+            {
+                unsigned long int_info;
+
+                __vmread(GUEST_INTERRUPTIBILITY_INFO, &int_info);
+
+                if ( int_info & (VMX_INTR_SHADOW_STI | VMX_INTR_SHADOW_MOV_SS) )
+                {
+                    unsigned long pending_dbg;
+
+                    __vmread(GUEST_PENDING_DBG_EXCEPTIONS, &pending_dbg);
+                    __vmwrite(GUEST_PENDING_DBG_EXCEPTIONS,
+                              pending_dbg | DR_STEP);
+                }
+            }
+
             if ( !v->domain->debugger_attached )
             {
                 unsigned long insn_len = 0;