2022-11-03 14:30:49

by Maxim Levitsky

[permalink] [raw]
Subject: [PATCH v2 0/9] nSVM: Security and correctness fixes

Recently while trying to fix some unit tests I found a CVE in SVM nested code.



In 'shutdown_interception' vmexit handler we call kvm_vcpu_reset.



However if running nested and L1 doesn't intercept shutdown, we will still end

up running this function and trigger a bug in it.



The bug is that this function resets the 'vcpu->arch.hflags' without properly

leaving the nested state, which leaves the vCPU in inconsistent state, which

later triggers a kernel panic in SVM code.



The same bug can likely be triggered by sending INIT via local apic to a vCPU

which runs a nested guest.



On VMX we are lucky that the issue can't happen because VMX always intercepts

triple faults, thus triple fault in L2 will always be redirected to L1.

Plus the 'handle_triple_fault' of VMX doesn't reset the vCPU.



INIT IPI can't happen on VMX either because INIT events are masked while in

VMX mode.



First 4 patches in this series address the above issue, and are

already posted on the list with title,

('nSVM: fix L0 crash if L2 has shutdown condtion which L1 doesn't intercept')

I addressed the review feedback and also added a unit test to hit this issue.



In addition to these patches I noticed that KVM doesn't honour SHUTDOWN intercept bit

of L1 on SVM, and I included a fix to do so - its only for correctness

as a normal hypervisor should always intercept SHUTDOWN.

A unit test on the other hand might want to not do so.

I also extendted the triple_fault_test selftest to hit this issue.



Finaly I found another security issue, I found a way to

trigger a kernel non rate limited printk on SVM from the guest, and

last patch in the series fixes that.



A unit test I posted to kvm-unit-tests project hits this issue, so

no selftest was added.



Best regards,

Maxim Levitsky



Maxim Levitsky (9):

KVM: x86: nSVM: leave nested mode on vCPU free

KVM: x86: nSVM: harden svm_free_nested against freeing vmcb02 while

still in use

KVM: x86: add kvm_leave_nested

KVM: x86: forcibly leave nested mode on vCPU reset

KVM: selftests: move idt_entry to header

kvm: selftests: add svm nested shutdown test

KVM: x86: allow L1 to not intercept triple fault

KVM: selftests: add svm part to triple_fault_test

KVM: x86: remove exit_int_info warning in svm_handle_exit



arch/x86/kvm/svm/nested.c | 12 ++-

arch/x86/kvm/svm/svm.c | 10 +--

arch/x86/kvm/vmx/nested.c | 4 +-

arch/x86/kvm/x86.c | 29 ++++++--

tools/testing/selftests/kvm/.gitignore | 1 +

tools/testing/selftests/kvm/Makefile | 1 +

.../selftests/kvm/include/x86_64/processor.h | 13 ++++

.../selftests/kvm/lib/x86_64/processor.c | 13 ----

.../kvm/x86_64/svm_nested_shutdown_test.c | 67 +++++++++++++++++

.../kvm/x86_64/triple_fault_event_test.c | 73 ++++++++++++++-----

10 files changed, 172 insertions(+), 51 deletions(-)

create mode 100644 tools/testing/selftests/kvm/x86_64/svm_nested_shutdown_test.c



--

2.34.3






2022-11-03 14:31:20

by Maxim Levitsky

[permalink] [raw]
Subject: [PATCH v2 6/9] kvm: selftests: add svm nested shutdown test

Add test that tests that on SVM if L1 doesn't intercept SHUTDOWN,
then L2 crashes L1 and doesn't crash L2

Signed-off-by: Maxim Levitsky <[email protected]>
---
tools/testing/selftests/kvm/.gitignore | 1 +
tools/testing/selftests/kvm/Makefile | 1 +
.../kvm/x86_64/svm_nested_shutdown_test.c | 67 +++++++++++++++++++
3 files changed, 69 insertions(+)
create mode 100644 tools/testing/selftests/kvm/x86_64/svm_nested_shutdown_test.c

diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
index 2f0d705db9dba5..05d980fb083d17 100644
--- a/tools/testing/selftests/kvm/.gitignore
+++ b/tools/testing/selftests/kvm/.gitignore
@@ -41,6 +41,7 @@
/x86_64/svm_vmcall_test
/x86_64/svm_int_ctl_test
/x86_64/svm_nested_soft_inject_test
+/x86_64/svm_nested_shutdown_test
/x86_64/sync_regs_test
/x86_64/tsc_msrs_test
/x86_64/tsc_scaling_sync
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 0172eb6cb6eee2..4a2caef2c9396f 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -101,6 +101,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/state_test
TEST_GEN_PROGS_x86_64 += x86_64/vmx_preemption_timer_test
TEST_GEN_PROGS_x86_64 += x86_64/svm_vmcall_test
TEST_GEN_PROGS_x86_64 += x86_64/svm_int_ctl_test
+TEST_GEN_PROGS_x86_64 += x86_64/svm_nested_shutdown_test
TEST_GEN_PROGS_x86_64 += x86_64/svm_nested_soft_inject_test
TEST_GEN_PROGS_x86_64 += x86_64/tsc_scaling_sync
TEST_GEN_PROGS_x86_64 += x86_64/sync_regs_test
diff --git a/tools/testing/selftests/kvm/x86_64/svm_nested_shutdown_test.c b/tools/testing/selftests/kvm/x86_64/svm_nested_shutdown_test.c
new file mode 100644
index 00000000000000..e73fcdef47bbe9
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86_64/svm_nested_shutdown_test.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * svm_nested_shutdown_test
+ *
+ * Copyright (C) 2022, Red Hat, Inc.
+ *
+ * Nested SVM testing: test that unintercepted shutdown in L2 doesn't crash the host
+ */
+
+#include "test_util.h"
+#include "kvm_util.h"
+#include "processor.h"
+#include "svm_util.h"
+
+static void l2_guest_code(struct svm_test_data *svm)
+{
+ __asm__ __volatile__("ud2");
+}
+
+static void l1_guest_code(struct svm_test_data *svm, struct idt_entry *idt)
+{
+ #define L2_GUEST_STACK_SIZE 64
+ unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE];
+ struct vmcb *vmcb = svm->vmcb;
+
+ generic_svm_setup(svm, l2_guest_code,
+ &l2_guest_stack[L2_GUEST_STACK_SIZE]);
+
+ vmcb->control.intercept &= ~(BIT(INTERCEPT_SHUTDOWN));
+
+ idt[6].p = 0; // #UD is intercepted but its injection will cause #NP
+ idt[11].p = 0; // #NP is not intercepted and will cause another
+ // #NP that will be converted to #DF
+ idt[8].p = 0; // #DF will cause #NP which will cause SHUTDOWN
+
+ run_guest(vmcb, svm->vmcb_gpa);
+
+ /* should not reach here */
+ GUEST_ASSERT(0);
+}
+
+int main(int argc, char *argv[])
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_run *run;
+ vm_vaddr_t svm_gva;
+ struct kvm_vm *vm;
+
+ TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM));
+
+ vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code);
+ vm_init_descriptor_tables(vm);
+ vcpu_init_descriptor_tables(vcpu);
+
+ vcpu_alloc_svm(vm, &svm_gva);
+
+ vcpu_args_set(vcpu, 2, svm_gva, vm->idt);
+ run = vcpu->run;
+
+ vcpu_run(vcpu);
+ TEST_ASSERT(run->exit_reason == KVM_EXIT_SHUTDOWN,
+ "Got exit_reason other than KVM_EXIT_SHUTDOWN: %u (%s)\n",
+ run->exit_reason,
+ exit_reason_str(run->exit_reason));
+
+ kvm_vm_free(vm);
+}
--
2.34.3


2022-11-03 14:52:55

by Maxim Levitsky

[permalink] [raw]
Subject: [PATCH v2 3/9] KVM: x86: add kvm_leave_nested

add kvm_leave_nested which wraps a call to nested_ops->leave_nested
into a function.

Cc: [email protected]
Signed-off-by: Maxim Levitsky <[email protected]>
---
arch/x86/kvm/svm/nested.c | 3 ---
arch/x86/kvm/vmx/nested.c | 3 ---
arch/x86/kvm/x86.c | 8 +++++++-
3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
index b74da40c1fc40c..bcc4f6620f8aec 100644
--- a/arch/x86/kvm/svm/nested.c
+++ b/arch/x86/kvm/svm/nested.c
@@ -1147,9 +1147,6 @@ void svm_free_nested(struct vcpu_svm *svm)
svm->nested.initialized = false;
}

-/*
- * Forcibly leave nested mode in order to be able to reset the VCPU later on.
- */
void svm_leave_nested(struct kvm_vcpu *vcpu)
{
struct vcpu_svm *svm = to_svm(vcpu);
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 61a2e551640a08..1ebe141a0a015f 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -6441,9 +6441,6 @@ static int vmx_get_nested_state(struct kvm_vcpu *vcpu,
return kvm_state.size;
}

-/*
- * Forcibly leave nested mode in order to be able to reset the VCPU later on.
- */
void vmx_leave_nested(struct kvm_vcpu *vcpu)
{
if (is_guest_mode(vcpu)) {
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index cd9eb13e2ed7fc..316ab1d5317f92 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -627,6 +627,12 @@ static void kvm_queue_exception_vmexit(struct kvm_vcpu *vcpu, unsigned int vecto
ex->payload = payload;
}

+/* Forcibly leave the nested mode in cases like a vCPU reset */
+static void kvm_leave_nested(struct kvm_vcpu *vcpu)
+{
+ kvm_x86_ops.nested_ops->leave_nested(vcpu);
+}
+
static void kvm_multiple_exception(struct kvm_vcpu *vcpu,
unsigned nr, bool has_error, u32 error_code,
bool has_payload, unsigned long payload, bool reinject)
@@ -5193,7 +5199,7 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
if (events->flags & KVM_VCPUEVENT_VALID_SMM) {
#ifdef CONFIG_KVM_SMM
if (!!(vcpu->arch.hflags & HF_SMM_MASK) != events->smi.smm) {
- kvm_x86_ops.nested_ops->leave_nested(vcpu);
+ kvm_leave_nested(vcpu);
kvm_smm_changed(vcpu, events->smi.smm);
}

--
2.34.3


2022-11-03 14:53:38

by Maxim Levitsky

[permalink] [raw]
Subject: Re: [PATCH v2 6/9] kvm: selftests: add svm nested shutdown test

On Thu, 2022-11-03 at 16:13 +0200, Maxim Levitsky wrote:
> Add test that tests that on SVM if L1 doesn't intercept SHUTDOWN,
> then L2 crashes L1 and doesn't crash L2
I mean doesn't crash L0, sorry for typo.

Best regards,
Maxim Levitsky
>
> Signed-off-by: Maxim Levitsky <[email protected]>
> ---
>  tools/testing/selftests/kvm/.gitignore        |  1 +
>  tools/testing/selftests/kvm/Makefile          |  1 +
>  .../kvm/x86_64/svm_nested_shutdown_test.c     | 67 +++++++++++++++++++
>  3 files changed, 69 insertions(+)
>  create mode 100644 tools/testing/selftests/kvm/x86_64/svm_nested_shutdown_test.c
>
> diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
> index 2f0d705db9dba5..05d980fb083d17 100644
> --- a/tools/testing/selftests/kvm/.gitignore
> +++ b/tools/testing/selftests/kvm/.gitignore
> @@ -41,6 +41,7 @@
>  /x86_64/svm_vmcall_test
>  /x86_64/svm_int_ctl_test
>  /x86_64/svm_nested_soft_inject_test
> +/x86_64/svm_nested_shutdown_test
>  /x86_64/sync_regs_test
>  /x86_64/tsc_msrs_test
>  /x86_64/tsc_scaling_sync
> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
> index 0172eb6cb6eee2..4a2caef2c9396f 100644
> --- a/tools/testing/selftests/kvm/Makefile
> +++ b/tools/testing/selftests/kvm/Makefile
> @@ -101,6 +101,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/state_test
>  TEST_GEN_PROGS_x86_64 += x86_64/vmx_preemption_timer_test
>  TEST_GEN_PROGS_x86_64 += x86_64/svm_vmcall_test
>  TEST_GEN_PROGS_x86_64 += x86_64/svm_int_ctl_test
> +TEST_GEN_PROGS_x86_64 += x86_64/svm_nested_shutdown_test
>  TEST_GEN_PROGS_x86_64 += x86_64/svm_nested_soft_inject_test
>  TEST_GEN_PROGS_x86_64 += x86_64/tsc_scaling_sync
>  TEST_GEN_PROGS_x86_64 += x86_64/sync_regs_test
> diff --git a/tools/testing/selftests/kvm/x86_64/svm_nested_shutdown_test.c b/tools/testing/selftests/kvm/x86_64/svm_nested_shutdown_test.c
> new file mode 100644
> index 00000000000000..e73fcdef47bbe9
> --- /dev/null
> +++ b/tools/testing/selftests/kvm/x86_64/svm_nested_shutdown_test.c
> @@ -0,0 +1,67 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * svm_nested_shutdown_test
> + *
> + * Copyright (C) 2022, Red Hat, Inc.
> + *
> + * Nested SVM testing: test that unintercepted shutdown in L2 doesn't crash the host
> + */
> +
> +#include "test_util.h"
> +#include "kvm_util.h"
> +#include "processor.h"
> +#include "svm_util.h"
> +
> +static void l2_guest_code(struct svm_test_data *svm)
> +{
> +       __asm__ __volatile__("ud2");
> +}
> +
> +static void l1_guest_code(struct svm_test_data *svm, struct idt_entry *idt)
> +{
> +       #define L2_GUEST_STACK_SIZE 64
> +       unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE];
> +       struct vmcb *vmcb = svm->vmcb;
> +
> +       generic_svm_setup(svm, l2_guest_code,
> +                         &l2_guest_stack[L2_GUEST_STACK_SIZE]);
> +
> +       vmcb->control.intercept &= ~(BIT(INTERCEPT_SHUTDOWN));
> +
> +       idt[6].p   = 0; // #UD is intercepted but its injection will cause #NP
> +       idt[11].p  = 0; // #NP is not intercepted and will cause another
> +                       // #NP that will be converted to #DF
> +       idt[8].p   = 0; // #DF will cause #NP which will cause SHUTDOWN
> +
> +       run_guest(vmcb, svm->vmcb_gpa);
> +
> +       /* should not reach here */
> +       GUEST_ASSERT(0);
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +       struct kvm_vcpu *vcpu;
> +       struct kvm_run *run;
> +       vm_vaddr_t svm_gva;
> +       struct kvm_vm *vm;
> +
> +       TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM));
> +
> +       vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code);
> +       vm_init_descriptor_tables(vm);
> +       vcpu_init_descriptor_tables(vcpu);
> +
> +       vcpu_alloc_svm(vm, &svm_gva);
> +
> +       vcpu_args_set(vcpu, 2, svm_gva, vm->idt);
> +       run = vcpu->run;
> +
> +       vcpu_run(vcpu);
> +       TEST_ASSERT(run->exit_reason == KVM_EXIT_SHUTDOWN,
> +                   "Got exit_reason other than KVM_EXIT_SHUTDOWN: %u (%s)\n",
> +                   run->exit_reason,
> +                   exit_reason_str(run->exit_reason));
> +
> +       kvm_vm_free(vm);
> +}



2022-11-03 14:54:30

by Maxim Levitsky

[permalink] [raw]
Subject: [PATCH v2 1/9] KVM: x86: nSVM: leave nested mode on vCPU free

If the VM was terminated while nested, we free the nested state
while the vCPU still is in nested mode.

Soon a warning will be added for this condition.

Cc: [email protected]
Signed-off-by: Maxim Levitsky <[email protected]>
---
arch/x86/kvm/svm/svm.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index d22a809d923339..e9cec1b692051c 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -1440,6 +1440,7 @@ static void svm_vcpu_free(struct kvm_vcpu *vcpu)
*/
svm_clear_current_vmcb(svm->vmcb);

+ svm_leave_nested(vcpu);
svm_free_nested(svm);

sev_free_vcpu(vcpu);
--
2.34.3


2022-11-03 15:25:19

by Maxim Levitsky

[permalink] [raw]
Subject: [PATCH v2 2/9] KVM: x86: nSVM: harden svm_free_nested against freeing vmcb02 while still in use

Make sure that KVM uses vmcb01 before freeing nested state, and warn if
that is not the case.

This is a minimal fix for CVE-2022-3344 making the kernel print a warning
instead of a kernel panic.

Cc: [email protected]
Signed-off-by: Maxim Levitsky <[email protected]>
---
arch/x86/kvm/svm/nested.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
index b258d6988f5dde..b74da40c1fc40c 100644
--- a/arch/x86/kvm/svm/nested.c
+++ b/arch/x86/kvm/svm/nested.c
@@ -1126,6 +1126,9 @@ void svm_free_nested(struct vcpu_svm *svm)
if (!svm->nested.initialized)
return;

+ if (WARN_ON_ONCE(svm->vmcb != svm->vmcb01.ptr))
+ svm_switch_vmcb(svm, &svm->vmcb01);
+
svm_vcpu_free_msrpm(svm->nested.msrpm);
svm->nested.msrpm = NULL;

--
2.34.3


2022-11-15 15:08:38

by Maxim Levitsky

[permalink] [raw]
Subject: Re: [PATCH v2 0/9] nSVM: Security and correctness fixes

On Thu, 2022-11-03 at 16:13 +0200, Maxim Levitsky wrote:
> Recently while trying to fix some unit tests I found a CVE in SVM nested code.
>
> In 'shutdown_interception' vmexit handler we call kvm_vcpu_reset.
>
> However if running nested and L1 doesn't intercept shutdown, we will still end
> up running this function and trigger a bug in it.
>
> The bug is that this function resets the 'vcpu->arch.hflags' without properly
> leaving the nested state, which leaves the vCPU in inconsistent state, which
> later triggers a kernel panic in SVM code.
>
> The same bug can likely be triggered by sending INIT via local apic to a vCPU
> which runs a nested guest.
>
> On VMX we are lucky that the issue can't happen because VMX always intercepts
> triple faults, thus triple fault in L2 will always be redirected to L1.
> Plus the 'handle_triple_fault' of VMX doesn't reset the vCPU.
>
> INIT IPI can't happen on VMX either because INIT events are masked while in
> VMX mode.
>
> First 4 patches in this series address the above issue, and are
> already posted on the list with title,
> ('nSVM: fix L0 crash if L2 has shutdown condtion which L1 doesn't intercept')
> I addressed the review feedback and also added a unit test to hit this issue.
>
> In addition to these patches I noticed that KVM doesn't honour SHUTDOWN intercept bit
> of L1 on SVM, and I included a fix to do so - its only for correctness
> as a normal hypervisor should always intercept SHUTDOWN.
> A unit test on the other hand might want to not do so.
> I also extendted the triple_fault_test selftest to hit this issue.
>
> Finaly I found another security issue, I found a way to
> trigger a kernel non rate limited printk on SVM from the guest, and
> last patch in the series fixes that.
>
> A unit test I posted to kvm-unit-tests project hits this issue, so
> no selftest was added.
>
> Best regards,
>         Maxim Levitsky
>
> Maxim Levitsky (9):
>   KVM: x86: nSVM: leave nested mode on vCPU free
>   KVM: x86: nSVM: harden svm_free_nested against freeing vmcb02 while
>     still in use
>   KVM: x86: add kvm_leave_nested
>   KVM: x86: forcibly leave nested mode on vCPU reset
>   KVM: selftests: move idt_entry to header
>   kvm: selftests: add svm nested shutdown test
>   KVM: x86: allow L1 to not intercept triple fault
>   KVM: selftests: add svm part to triple_fault_test
>   KVM: x86: remove exit_int_info warning in svm_handle_exit
>
>  arch/x86/kvm/svm/nested.c                     | 12 ++-
>  arch/x86/kvm/svm/svm.c                        | 10 +--
>  arch/x86/kvm/vmx/nested.c                     |  4 +-
>  arch/x86/kvm/x86.c                            | 29 ++++++--
>  tools/testing/selftests/kvm/.gitignore        |  1 +
>  tools/testing/selftests/kvm/Makefile          |  1 +
>  .../selftests/kvm/include/x86_64/processor.h  | 13 ++++
>  .../selftests/kvm/lib/x86_64/processor.c      | 13 ----
>  .../kvm/x86_64/svm_nested_shutdown_test.c     | 67 +++++++++++++++++
>  .../kvm/x86_64/triple_fault_event_test.c      | 73 ++++++++++++++-----
>  10 files changed, 172 insertions(+), 51 deletions(-)
>  create mode 100644 tools/testing/selftests/kvm/x86_64/svm_nested_shutdown_test.c
>
> --

Kind ping on the patch series.


Best regards,
Maxim Levitsky

> 2.34.3
>
>



2022-11-21 16:53:14

by Liam Merwick

[permalink] [raw]
Subject: Re: [PATCH v2 3/9] KVM: x86: add kvm_leave_nested

On 03/11/2022 14:13, Maxim Levitsky wrote:
> add kvm_leave_nested which wraps a call to nested_ops->leave_nested
> into a function.
>
> Cc: [email protected]
> Signed-off-by: Maxim Levitsky <[email protected]>

Reviewed-by: Liam Merwick <[email protected]>


> ---
> arch/x86/kvm/svm/nested.c | 3 ---
> arch/x86/kvm/vmx/nested.c | 3 ---
> arch/x86/kvm/x86.c | 8 +++++++-
> 3 files changed, 7 insertions(+), 7 deletions(-)
>
> diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
> index b74da40c1fc40c..bcc4f6620f8aec 100644
> --- a/arch/x86/kvm/svm/nested.c
> +++ b/arch/x86/kvm/svm/nested.c
> @@ -1147,9 +1147,6 @@ void svm_free_nested(struct vcpu_svm *svm)
> svm->nested.initialized = false;
> }
>
> -/*
> - * Forcibly leave nested mode in order to be able to reset the VCPU later on.
> - */
> void svm_leave_nested(struct kvm_vcpu *vcpu)
> {
> struct vcpu_svm *svm = to_svm(vcpu);
> diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
> index 61a2e551640a08..1ebe141a0a015f 100644
> --- a/arch/x86/kvm/vmx/nested.c
> +++ b/arch/x86/kvm/vmx/nested.c
> @@ -6441,9 +6441,6 @@ static int vmx_get_nested_state(struct kvm_vcpu *vcpu,
> return kvm_state.size;
> }
>
> -/*
> - * Forcibly leave nested mode in order to be able to reset the VCPU later on.
> - */
> void vmx_leave_nested(struct kvm_vcpu *vcpu)
> {
> if (is_guest_mode(vcpu)) {
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index cd9eb13e2ed7fc..316ab1d5317f92 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -627,6 +627,12 @@ static void kvm_queue_exception_vmexit(struct kvm_vcpu *vcpu, unsigned int vecto
> ex->payload = payload;
> }
>
> +/* Forcibly leave the nested mode in cases like a vCPU reset */
> +static void kvm_leave_nested(struct kvm_vcpu *vcpu)
> +{
> + kvm_x86_ops.nested_ops->leave_nested(vcpu);
> +}
> +
> static void kvm_multiple_exception(struct kvm_vcpu *vcpu,
> unsigned nr, bool has_error, u32 error_code,
> bool has_payload, unsigned long payload, bool reinject)
> @@ -5193,7 +5199,7 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
> if (events->flags & KVM_VCPUEVENT_VALID_SMM) {
> #ifdef CONFIG_KVM_SMM
> if (!!(vcpu->arch.hflags & HF_SMM_MASK) != events->smi.smm) {
> - kvm_x86_ops.nested_ops->leave_nested(vcpu);
> + kvm_leave_nested(vcpu);
> kvm_smm_changed(vcpu, events->smi.smm);
> }
>


2022-11-21 16:58:46

by Liam Merwick

[permalink] [raw]
Subject: Re: [PATCH v2 6/9] kvm: selftests: add svm nested shutdown test

On 03/11/2022 14:28, Maxim Levitsky wrote:
> On Thu, 2022-11-03 at 16:13 +0200, Maxim Levitsky wrote:
>> Add test that tests that on SVM if L1 doesn't intercept SHUTDOWN,
>> then L2 crashes L1 and doesn't crash L2
> I mean doesn't crash L0, sorry for typo.
>
> Best regards,
> Maxim Levitsky
>>
>> Signed-off-by: Maxim Levitsky <[email protected]>


Reviewed-by: Liam Merwick <[email protected]>


>> ---
>>  tools/testing/selftests/kvm/.gitignore        |  1 +
>>  tools/testing/selftests/kvm/Makefile          |  1 +
>>  .../kvm/x86_64/svm_nested_shutdown_test.c     | 67 +++++++++++++++++++
>>  3 files changed, 69 insertions(+)
>>  create mode 100644 tools/testing/selftests/kvm/x86_64/svm_nested_shutdown_test.c
>>
>> diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
>> index 2f0d705db9dba5..05d980fb083d17 100644
>> --- a/tools/testing/selftests/kvm/.gitignore
>> +++ b/tools/testing/selftests/kvm/.gitignore
>> @@ -41,6 +41,7 @@
>>  /x86_64/svm_vmcall_test
>>  /x86_64/svm_int_ctl_test
>>  /x86_64/svm_nested_soft_inject_test
>> +/x86_64/svm_nested_shutdown_test
>>  /x86_64/sync_regs_test
>>  /x86_64/tsc_msrs_test
>>  /x86_64/tsc_scaling_sync
>> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
>> index 0172eb6cb6eee2..4a2caef2c9396f 100644
>> --- a/tools/testing/selftests/kvm/Makefile
>> +++ b/tools/testing/selftests/kvm/Makefile
>> @@ -101,6 +101,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/state_test
>>  TEST_GEN_PROGS_x86_64 += x86_64/vmx_preemption_timer_test
>>  TEST_GEN_PROGS_x86_64 += x86_64/svm_vmcall_test
>>  TEST_GEN_PROGS_x86_64 += x86_64/svm_int_ctl_test
>> +TEST_GEN_PROGS_x86_64 += x86_64/svm_nested_shutdown_test
>>  TEST_GEN_PROGS_x86_64 += x86_64/svm_nested_soft_inject_test
>>  TEST_GEN_PROGS_x86_64 += x86_64/tsc_scaling_sync
>>  TEST_GEN_PROGS_x86_64 += x86_64/sync_regs_test
>> diff --git a/tools/testing/selftests/kvm/x86_64/svm_nested_shutdown_test.c b/tools/testing/selftests/kvm/x86_64/svm_nested_shutdown_test.c
>> new file mode 100644
>> index 00000000000000..e73fcdef47bbe9
>> --- /dev/null
>> +++ b/tools/testing/selftests/kvm/x86_64/svm_nested_shutdown_test.c
>> @@ -0,0 +1,67 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * svm_nested_shutdown_test
>> + *
>> + * Copyright (C) 2022, Red Hat, Inc.
>> + *
>> + * Nested SVM testing: test that unintercepted shutdown in L2 doesn't crash the host
>> + */
>> +
>> +#include "test_util.h"
>> +#include "kvm_util.h"
>> +#include "processor.h"
>> +#include "svm_util.h"
>> +
>> +static void l2_guest_code(struct svm_test_data *svm)
>> +{
>> +       __asm__ __volatile__("ud2");
>> +}
>> +
>> +static void l1_guest_code(struct svm_test_data *svm, struct idt_entry *idt)
>> +{
>> +       #define L2_GUEST_STACK_SIZE 64
>> +       unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE];
>> +       struct vmcb *vmcb = svm->vmcb;
>> +
>> +       generic_svm_setup(svm, l2_guest_code,
>> +                         &l2_guest_stack[L2_GUEST_STACK_SIZE]);
>> +
>> +       vmcb->control.intercept &= ~(BIT(INTERCEPT_SHUTDOWN));
>> +
>> +       idt[6].p   = 0; // #UD is intercepted but its injection will cause #NP
>> +       idt[11].p  = 0; // #NP is not intercepted and will cause another
>> +                       // #NP that will be converted to #DF
>> +       idt[8].p   = 0; // #DF will cause #NP which will cause SHUTDOWN
>> +
>> +       run_guest(vmcb, svm->vmcb_gpa);
>> +
>> +       /* should not reach here */
>> +       GUEST_ASSERT(0);
>> +}
>> +
>> +int main(int argc, char *argv[])
>> +{
>> +       struct kvm_vcpu *vcpu;
>> +       struct kvm_run *run;
>> +       vm_vaddr_t svm_gva;
>> +       struct kvm_vm *vm;
>> +
>> +       TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM));
>> +
>> +       vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code);
>> +       vm_init_descriptor_tables(vm);
>> +       vcpu_init_descriptor_tables(vcpu);
>> +
>> +       vcpu_alloc_svm(vm, &svm_gva);
>> +
>> +       vcpu_args_set(vcpu, 2, svm_gva, vm->idt);
>> +       run = vcpu->run;
>> +
>> +       vcpu_run(vcpu);
>> +       TEST_ASSERT(run->exit_reason == KVM_EXIT_SHUTDOWN,
>> +                   "Got exit_reason other than KVM_EXIT_SHUTDOWN: %u (%s)\n",
>> +                   run->exit_reason,
>> +                   exit_reason_str(run->exit_reason));
>> +
>> +       kvm_vm_free(vm);
>> +}
>
>


2022-11-21 16:59:42

by Liam Merwick

[permalink] [raw]
Subject: Re: [PATCH v2 2/9] KVM: x86: nSVM: harden svm_free_nested against freeing vmcb02 while still in use

On 03/11/2022 14:13, Maxim Levitsky wrote:
> Make sure that KVM uses vmcb01 before freeing nested state, and warn if
> that is not the case.
>
> This is a minimal fix for CVE-2022-3344 making the kernel print a warning
> instead of a kernel panic.
>
> Cc: [email protected]
> Signed-off-by: Maxim Levitsky <[email protected]>

Reviewed-by: Liam Merwick <[email protected]>


> ---
> arch/x86/kvm/svm/nested.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
> index b258d6988f5dde..b74da40c1fc40c 100644
> --- a/arch/x86/kvm/svm/nested.c
> +++ b/arch/x86/kvm/svm/nested.c
> @@ -1126,6 +1126,9 @@ void svm_free_nested(struct vcpu_svm *svm)
> if (!svm->nested.initialized)
> return;
>
> + if (WARN_ON_ONCE(svm->vmcb != svm->vmcb01.ptr))
> + svm_switch_vmcb(svm, &svm->vmcb01);
> +
> svm_vcpu_free_msrpm(svm->nested.msrpm);
> svm->nested.msrpm = NULL;
>


2022-11-21 17:22:02

by Liam Merwick

[permalink] [raw]
Subject: Re: [PATCH v2 1/9] KVM: x86: nSVM: leave nested mode on vCPU free

On 03/11/2022 14:13, Maxim Levitsky wrote:
> If the VM was terminated while nested, we free the nested state
> while the vCPU still is in nested mode.
>
> Soon a warning will be added for this condition.
>
> Cc: [email protected]
> Signed-off-by: Maxim Levitsky <[email protected]>

Reviewed-by: Liam Merwick <[email protected]>

> ---
> arch/x86/kvm/svm/svm.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
> index d22a809d923339..e9cec1b692051c 100644
> --- a/arch/x86/kvm/svm/svm.c
> +++ b/arch/x86/kvm/svm/svm.c
> @@ -1440,6 +1440,7 @@ static void svm_vcpu_free(struct kvm_vcpu *vcpu)
> */
> svm_clear_current_vmcb(svm->vmcb);
>
> + svm_leave_nested(vcpu);
> svm_free_nested(svm);
>
> sev_free_vcpu(vcpu);