From: Andrew Cooper <andrew.cooper3@citrix.com>
Subject: x86/vmx: Rework #DB intercept logic to fix VMEntry failure with BTF

The consistency checks in effect when Blocked-by-STI/MovSS are active includes
DEBUGCTL.BTF too.  Take this into account when editing PENDING_DBG.BS.

This ends up narrowing the cases where PENDING_DBG is modified.  Rework the
comment to be more precise.

This is XSA-518 / CVE-2026-79611.

Fixes: 1d3eb8259804 ("x86/vtx: Work around SingleStep + STI/MovSS VMEntry failures")
Reported-by: Teddy Astie <teddy.astie@vates.tech>
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Teddy Astie <teddy.astie@vates.tech>
---
 xen/arch/x86/hvm/vmx/vmx.c | 41 +++++++++++++++++---------------------
 1 file changed, 18 insertions(+), 23 deletions(-)

diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c
index 6237623a4c16..97dd37dd7ce4 100644
--- a/xen/arch/x86/hvm/vmx/vmx.c
+++ b/xen/arch/x86/hvm/vmx/vmx.c
@@ -4396,33 +4396,28 @@ void asmlinkage vmx_vmexit_handler(struct cpu_user_regs *regs)
              * 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.
+             * Most sources of #DB accumuate status in PENDING_DBG, which are
+             * then dispatched in priority order prior to fetching the next
+             * instruction.  When the #DB intercept triggers, PENDING_DBG is 0
+             * with the status bits having moved into EXIT_QUALIFICATION.
              *
-             * 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.
+             * At any point if Blocked-by-STI/MovSS are active, extra VMEntry
+             * consistency checks are applied, cross-checking EFLAGS.TF,
+             * PENDING_DBG.BS and DEBUGCTL.BTF.  These consistency checks
+             * apply to Activity-state-HLT too, but Xen doesn't support this.
              *
-             * 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.
+             * The VMCS state at the point of #DB intercept does not satisfy
+             * the consistency checks.  In the case that a SingleStep should
+             * be pending, fill PENDING_DBG back in to satisfy the consitency
+             * check.
              */
-            if ( unlikely(regs->eflags & X86_EFLAGS_TF) )
+            if ( unlikely(regs->eflags & X86_EFLAGS_TF) &&
+                 (vmread(GUEST_INTERRUPTIBILITY_INFO) &
+                  (VMX_INTR_SHADOW_STI | VMX_INTR_SHADOW_MOV_SS)) &&
+                 !(vmread(GUEST_IA32_DEBUGCTL) & IA32_DEBUGCTLMSR_BTF) )
             {
-                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);
-                }
+                ASSERT(vmread(GUEST_PENDING_DBG_EXCEPTIONS) == 0);
+                __vmwrite(GUEST_PENDING_DBG_EXCEPTIONS, X86_DR6_BS);
             }
 
             if ( !v->domain->debugger_attached )
