2023-10-18 19:20:42

by Sean Christopherson

[permalink] [raw]
Subject: [PATCH v2] KVM: SVM: Don't intercept IRET when injecting NMI and vNMI is enabled

When vNMI is enabled, rely entirely on hardware to correctly handle NMI
blocking, i.e. don't intercept IRET to detect when NMIs are no longer
blocked. KVM already correctly ignores svm->nmi_masked when vNMI is
enabled, so the effect of the bug is essentially an unnecessary VM-Exit.

KVM intercepts IRET for two reasons:
- To track NMI masking to be able to know at any point of time if NMI
is masked.
- To track NMI windows (to inject another NMI after the guest executes
IRET, i.e. unblocks NMIs)

When vNMI is enabled, both cases are handled by hardware:
- NMI masking state resides in int_ctl.V_NMI_BLOCKING and can be read by
KVM at will.
- Hardware automatically "injects" pending virtual NMIs when virtual NMIs
become unblocked.

However, even though pending a virtual NMI for hardware to handle is the
most common way to synthesize a guest NMI, KVM may still directly inject
an NMI via when KVM is handling two "simultaneous" NMIs (see comments in
process_nmi() for details on KVM's simultaneous NMI handling). Per AMD's
APM, hardware sets the BLOCKING flag when software directly injects an NMI
as well, i.e. KVM doesn't need to manually mark vNMIs as blocked:

If Event Injection is used to inject an NMI when NMI Virtualization is
enabled, VMRUN sets V_NMI_MASK in the guest state.

Note, it's still possible that KVM could trigger a spurious IRET VM-Exit.
When running a nested guest, KVM disables vNMI for L2 and thus will enable
IRET interception (in both vmcb01 and vmcb02) while running L2 reason. If
a nested VM-Exit happens before L2 executes IRET, KVM can end up running
L1 with vNMI enable and IRET intercepted. This is also a benign bug, and
even less likely to happen, i.e. can be safely punted to a future fix.

Fixes: fa4c027a7956 ("KVM: x86: Add support for SVM's Virtual NMI")
Link: https://lore.kernel.org/all/[email protected]
Cc: Santosh Shukla <[email protected]>
Cc: Maxim Levitsky <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
---

v2: Expand changelog to explain the various behaviors and combos. [Maxim]

v1: https://lore.kernel.org/all/[email protected]

arch/x86/kvm/svm/svm.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 1785de7dc98b..517a12e0f1fd 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -3568,8 +3568,15 @@ static void svm_inject_nmi(struct kvm_vcpu *vcpu)
if (svm->nmi_l1_to_l2)
return;

- svm->nmi_masked = true;
- svm_set_iret_intercept(svm);
+ /*
+ * No need to manually track NMI masking when vNMI is enabled, hardware
+ * automatically sets V_NMI_BLOCKING_MASK as appropriate, including the
+ * case where software directly injects an NMI.
+ */
+ if (!is_vnmi_enabled(svm)) {
+ svm->nmi_masked = true;
+ svm_set_iret_intercept(svm);
+ }
++vcpu->stat.nmi_injections;
}


base-commit: 437bba5ad2bba00c2056c896753a32edf80860cc
--
2.42.0.655.g421f12c284-goog


2023-10-22 15:01:05

by Santosh Shukla

[permalink] [raw]
Subject: Re: [PATCH v2] KVM: SVM: Don't intercept IRET when injecting NMI and vNMI is enabled



On 10/19/2023 12:50 AM, Sean Christopherson wrote:
> When vNMI is enabled, rely entirely on hardware to correctly handle NMI
> blocking, i.e. don't intercept IRET to detect when NMIs are no longer
> blocked. KVM already correctly ignores svm->nmi_masked when vNMI is
> enabled, so the effect of the bug is essentially an unnecessary VM-Exit.
>
> KVM intercepts IRET for two reasons:
> - To track NMI masking to be able to know at any point of time if NMI
> is masked.
> - To track NMI windows (to inject another NMI after the guest executes
> IRET, i.e. unblocks NMIs)
>
> When vNMI is enabled, both cases are handled by hardware:
> - NMI masking state resides in int_ctl.V_NMI_BLOCKING and can be read by
> KVM at will.
> - Hardware automatically "injects" pending virtual NMIs when virtual NMIs
> become unblocked.
>
> However, even though pending a virtual NMI for hardware to handle is the
> most common way to synthesize a guest NMI, KVM may still directly inject
> an NMI via when KVM is handling two "simultaneous" NMIs (see comments in
> process_nmi() for details on KVM's simultaneous NMI handling). Per AMD's
> APM, hardware sets the BLOCKING flag when software directly injects an NMI
> as well, i.e. KVM doesn't need to manually mark vNMIs as blocked:
>
> If Event Injection is used to inject an NMI when NMI Virtualization is
> enabled, VMRUN sets V_NMI_MASK in the guest state.
>
> Note, it's still possible that KVM could trigger a spurious IRET VM-Exit.
> When running a nested guest, KVM disables vNMI for L2 and thus will enable
> IRET interception (in both vmcb01 and vmcb02) while running L2 reason. If
> a nested VM-Exit happens before L2 executes IRET, KVM can end up running
> L1 with vNMI enable and IRET intercepted. This is also a benign bug, and
> even less likely to happen, i.e. can be safely punted to a future fix.
>
> Fixes: fa4c027a7956 ("KVM: x86: Add support for SVM's Virtual NMI")
> Link: https://lore.kernel.org/all/[email protected]
> Cc: Santosh Shukla <[email protected]>
> Cc: Maxim Levitsky <[email protected]>
> Signed-off-by: Sean Christopherson <[email protected]>
> ---
>
> v2: Expand changelog to explain the various behaviors and combos. [Maxim]
>
> v1: https://lore.kernel.org/all/[email protected]
>

Tested-by: Santosh Shukla <[email protected]>

Thanks,

2023-12-01 01:56:53

by Sean Christopherson

[permalink] [raw]
Subject: Re: [PATCH v2] KVM: SVM: Don't intercept IRET when injecting NMI and vNMI is enabled

On Wed, 18 Oct 2023 12:20:21 -0700, Sean Christopherson wrote:
> When vNMI is enabled, rely entirely on hardware to correctly handle NMI
> blocking, i.e. don't intercept IRET to detect when NMIs are no longer
> blocked. KVM already correctly ignores svm->nmi_masked when vNMI is
> enabled, so the effect of the bug is essentially an unnecessary VM-Exit.
>
> KVM intercepts IRET for two reasons:
> - To track NMI masking to be able to know at any point of time if NMI
> is masked.
> - To track NMI windows (to inject another NMI after the guest executes
> IRET, i.e. unblocks NMIs)
>
> [...]

Applied to kvm-x86 svm, thanks!

[1/1] KVM: SVM: Don't intercept IRET when injecting NMI and vNMI is enabled
https://github.com/kvm-x86/linux/commit/72046d0a077a

--
https://github.com/kvm-x86/linux/tree/next