|
[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
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |