VM-entry is disallowed in the shadow of a MOV-to-SS instruction. When the current-VMCS is valid, check that the instruction pointer falls through to the next instruction, the ALU flags are set to ZF (VMfailValid), and the VM-instruction error field contains 26 ("VM entry with events blocked by MOV SS."). Signed-off-by: Jim Mattson <jmattson@xxxxxxxxxx> --- lib/x86/processor.h | 16 +++++---- x86/unittests.cfg | 6 ++++ x86/vmx_tests.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 7 deletions(-) diff --git a/lib/x86/processor.h b/lib/x86/processor.h index 895d992a6943..631aa843b65e 100644 --- a/lib/x86/processor.h +++ b/lib/x86/processor.h @@ -30,13 +30,15 @@ #define X86_CR4_SMAP 0x00200000 #define X86_CR4_PKE 0x00400000 -#define X86_EFLAGS_CF 0x00000001 -#define X86_EFLAGS_PF 0x00000004 -#define X86_EFLAGS_AF 0x00000010 -#define X86_EFLAGS_ZF 0x00000040 -#define X86_EFLAGS_SF 0x00000080 -#define X86_EFLAGS_OF 0x00000800 -#define X86_EFLAGS_AC 0x00040000 +#define X86_EFLAGS_CF 0x00000001 +#define X86_EFLAGS_FIXED 0x00000002 +#define X86_EFLAGS_PF 0x00000004 +#define X86_EFLAGS_AF 0x00000010 +#define X86_EFLAGS_ZF 0x00000040 +#define X86_EFLAGS_SF 0x00000080 +#define X86_EFLAGS_IF 0x00000200 +#define X86_EFLAGS_OF 0x00000800 +#define X86_EFLAGS_AC 0x00040000 #define X86_IA32_EFER 0xc0000080 #define X86_EFER_LMA (1UL << 8) diff --git a/x86/unittests.cfg b/x86/unittests.cfg index 5ab46671d631..c5b35e7eee3e 100644 --- a/x86/unittests.cfg +++ b/x86/unittests.cfg @@ -494,6 +494,12 @@ extra_params = -cpu host,+vmx -m 2048 -append ept_access_test_force_2m_page arch = x86_64 groups = vmx +[vmx_vmentry_movss_shadow_test] +file = vmx.flat +extra_params = -cpu host,+vmx -m 2048 -append vmentry_movss_shadow_test +arch = x86_64 +groups = vmx + [debug] file = debug.flat arch = x86_64 diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c index 03e4ad43eba3..68ce0e09531a 100644 --- a/x86/vmx_tests.c +++ b/x86/vmx_tests.c @@ -2916,6 +2916,101 @@ static void ept_access_test_force_2m_page(void) ept_misconfig_at_level_mkhuge(true, 2, EPT_PRESENT, EPT_WA); } +static bool valid_vmcs_for_vmentry(void) +{ + struct vmcs *current_vmcs = NULL; + + if (vmcs_save(¤t_vmcs)) + return false; + + return current_vmcs && !(current_vmcs->revision_id >> 31); +} + +static void try_vmentry_in_movss_shadow(void) +{ + u32 vm_inst_err; + u32 flags; + bool early_failure = false; + u32 expected_flags = X86_EFLAGS_FIXED; + bool valid_vmcs = valid_vmcs_for_vmentry(); + + expected_flags |= valid_vmcs ? X86_EFLAGS_ZF : X86_EFLAGS_CF; + + /* + * Indirectly set VM_INST_ERR to 12 ("VMREAD/VMWRITE from/to + * unsupported VMCS component"). + */ + vmcs_write(~0u, 0); + + __asm__ __volatile__ ("mov %[host_rsp], %%edx;" + "vmwrite %%rsp, %%rdx;" + "mov 0f, %%rax;" + "mov %[host_rip], %%edx;" + "vmwrite %%rax, %%rdx;" + "mov $-1, %%ah;" + "sahf;" + "mov %%ss, %%ax;" + "mov %%ax, %%ss;" + "vmlaunch;" + "mov $1, %[early_failure];" + "0: lahf;" + "movzbl %%ah, %[flags]" + : [early_failure] "+r" (early_failure), + [flags] "=&a" (flags) + : [host_rsp] "i" (HOST_RSP), + [host_rip] "i" (HOST_RIP) + : "rdx", "cc", "memory"); + vm_inst_err = vmcs_read(VMX_INST_ERROR); + + report("Early VM-entry failure", early_failure); + report("RFLAGS[8:0] is %x (actual %x)", flags == expected_flags, + expected_flags, flags); + if (valid_vmcs) + report("VM-instruction error is %d (actual %d)", + vm_inst_err == VMXERR_ENTRY_EVENTS_BLOCKED_BY_MOV_SS, + VMXERR_ENTRY_EVENTS_BLOCKED_BY_MOV_SS, vm_inst_err); +} + +static void vmentry_movss_shadow_test(void) +{ + struct vmcs *orig_vmcs; + + TEST_ASSERT(!vmcs_save(&orig_vmcs)); + + /* + * Set the launched flag on the current VMCS to verify the correct + * error priority, below. + */ + test_set_guest(v2_null_test_guest); + enter_guest(); + + /* + * With bit 1 of the guest's RFLAGS clear, VM-entry should + * fail due to invalid guest state (if we make it that far). + */ + vmcs_write(GUEST_RFLAGS, 0); + + /* + * "VM entry with events blocked by MOV SS" takes precedence over + * "VMLAUNCH with non-clear VMCS." + */ + report_prefix_push("valid current-VMCS"); + try_vmentry_in_movss_shadow(); + report_prefix_pop(); + + /* + * VMfailInvalid takes precedence over "VM entry with events + * blocked by MOV SS." + */ + TEST_ASSERT(!vmcs_clear(orig_vmcs)); + report_prefix_push("no current-VMCS"); + try_vmentry_in_movss_shadow(); + report_prefix_pop(); + + TEST_ASSERT(!make_vmcs_current(orig_vmcs)); + vmcs_write(GUEST_RFLAGS, X86_EFLAGS_FIXED); +} + #define TEST(name) { #name, .v2 = name } /* name/init/guest_main/exit_handler/syscall_handler/guest_regs */ @@ -2977,5 +3072,6 @@ struct vmx_test vmx_tests[] = { TEST(ept_access_test_paddr_read_execute_ad_enabled), TEST(ept_access_test_paddr_not_present_page_fault), TEST(ept_access_test_force_2m_page), + TEST(vmentry_movss_shadow_test), { NULL, NULL, NULL, NULL, NULL, {0} }, }; -- 2.13.2.932.g7449e964c-goog