From: Andrew Cooper 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 Signed-off-by: Andrew Cooper Reviewed-by: Jan Beulich Reviewed-by: Teddy Astie --- 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 )