[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] x86/nsvm: Fix L2 VINTR intercept merging when only Xen intercepts L1



When nesting SVM, if the L1 hypervisor has not requested a virtual interrupt
(VINTR) intercept for L2, Xen should not force one. L2 execution should remain
isolated from the L1 VINTR state, and there should be no expectation that L2
execution results in an L1 VINTR becoming deliverable.

Currently, if Xen intercepts the VINTR for its own L1 management, this
intercept incorrectly forces an intercept during L2 execution even when L1
intended for L2 to run without it. This breaks the expected transparency of L1
configuration and introduces unnecessary VMEXITs.

Furthermore, handling this forced VMEXIT_VINTR can cause a pending L1 virtual
interrupt to be incorrectly delivered to L2. This occurs because when the
intercept is determined to be requested solely by the host
(returning NESTEDHVM_VMEXIT_HOST), svm_vmexit_handler does not switch to the
correct VMCB context before evaluating the interrupt injection.

Fix this by ensuring the L2 VINTR intercept bit in the L2 execution VMCB solely
reflects the L1's explicit configuration for its guest, independent of Xen's
internal L1 intercept state.

Signed-off-by: Abdelkareem Abdelsaamad <abdelkareem.abdelsaamad@xxxxxxxxxx>
---
Testing:
 - Test setup using a custom XTF nested virtualization setup:
   1. An L1 maskable interrupt is triggered while L1 has physical interrupts
      disabled (via the CLI), leaving the interrupt pending.
   2. L2 is launched via VMRUN with an active, deliverable virtual interrupt
      configured in its VMCB (L2 EFLAGS.IF = 1, L2 vGIF is unmasked,
      V_IRQ_PENDING is set and L2 V_IRQ is injected on a distinct vector from
      the pending L1 interrupt).
   3. Xen enables L1's VINTR_INTERCEPT to catch the next available VINTR
      window. L2's VINTR_INTERCEPT is kept cleared.

   Results:
     - Old Behaviour: If the pending L1 maskable interrupt vector has no
       handler mapped in the L2 IDT, L2 immediately crashes. Otherwise,
       the L1 maskable interrupt is incorrectly leaked and delivered into
       the L2 guest.
     - New Behaviour: The L1 maskable interrupt is securely withheld from L2
       and correctly delivered back to the L1 context upon the next window.

   Note on Test scenario:
   I think the same window race can also result when having an injected L1
   maskable interrupt handler that temporarily raise the TPR to block
   lower-priority interrupts, with the intention to be lowering the TPR
   asynchronously later, then executing the L2 VMRUN in between.

 - CI tests:
https://gitlab.com/xen-project/people/aabdelsa/xen/-/pipelines/3023453459
---
 xen/arch/x86/hvm/svm/nestedsvm.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/xen/arch/x86/hvm/svm/nestedsvm.c b/xen/arch/x86/hvm/svm/nestedsvm.c
index 128008321d..21b50a59b0 100644
--- a/xen/arch/x86/hvm/svm/nestedsvm.c
+++ b/xen/arch/x86/hvm/svm/nestedsvm.c
@@ -403,6 +403,9 @@ static int nsvm_vmcb_prepare4vmrun(struct vcpu *v, struct 
cpu_user_regs *regs)
     n2vmcb->_general2_intercepts =
         n1vmcb->_general2_intercepts | ns_vmcb->_general2_intercepts;
 
+    if ( !(ns_vmcb->_general1_intercepts & GENERAL1_INTERCEPT_VINTR) )
+        n2vmcb->_general1_intercepts &= ~GENERAL1_INTERCEPT_VINTR;
+
     /* Nested Pause Filter */
     if ( ns_vmcb->_general1_intercepts & GENERAL1_INTERCEPT_PAUSE )
         n2vmcb->_pause_filter_count =
-- 
2.53.0




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.