Use ASM_TRY() in the VMREAD and VMWRITE page fault tests to fix a bug where gcc-12 completely optimizes out handler_called. Because the flag isn't tagged volatile and the compiler is unaware that an exception may occur, gcc-12 thinks the value can only ever be 0. Note, exception fixup effectively performs an exact RIP check, and using LAHF to save RFLAGS drops the fixed RFLAGS bit. Opportunistically drop the 'noinline' as removing the global label makes the functions safe to inline/duplicate. Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx> --- x86/vmx.c | 135 ++++++++++++++++++++---------------------------------- 1 file changed, 49 insertions(+), 86 deletions(-) diff --git a/x86/vmx.c b/x86/vmx.c index a13f2c9..0ae134d 100644 --- a/x86/vmx.c +++ b/x86/vmx.c @@ -387,25 +387,7 @@ static void test_vmwrite_vmread(void) free_page(vmcs); } -ulong finish_fault; -u8 sentinel; -bool handler_called; - -static void pf_handler(struct ex_regs *regs) -{ - /* - * check that RIP was not improperly advanced and that the - * flags value was preserved. - */ - report(regs->rip < finish_fault, "RIP has not been advanced!"); - report(((u8)regs->rflags == ((sentinel | 2) & 0xd7)), - "The low byte of RFLAGS was preserved!"); - regs->rip = finish_fault; - handler_called = true; - -} - -static void prep_flags_test_env(void **vpage, struct vmcs **vmcs, handler *old) +static void prep_flags_test_env(void **vpage, struct vmcs **vmcs) { /* * get an unbacked address that will cause a #PF @@ -421,107 +403,88 @@ static void prep_flags_test_env(void **vpage, struct vmcs **vmcs, handler *old) (*vmcs)->hdr.revision_id = basic.revision; assert(!vmcs_clear(*vmcs)); assert(!make_vmcs_current(*vmcs)); - - *old = handle_exception(PF_VECTOR, &pf_handler); } -static noinline void test_read_sentinel(void) +static void test_read_sentinel(u8 sentinel) { - void *vpage; + unsigned long flags = sentinel; + unsigned int vector; struct vmcs *vmcs; - handler old; + void *vpage; - prep_flags_test_env(&vpage, &vmcs, &old); + prep_flags_test_env(&vpage, &vmcs); /* - * set the proper label + * Execute VMREAD with a not-PRESENT memory operand, and verify a #PF + * occurred and RFLAGS were not modified. */ - extern char finish_read_fault; + asm volatile ("sahf\n\t" + ASM_TRY("1f") + "vmread %[enc], %[val]\n\t" + "1: lahf" + : [val] "=m" (*(u64 *)vpage), + [flags] "+a" (flags) + : [enc] "r" ((u64)GUEST_SEL_SS) + : "cc"); - finish_fault = (ulong)&finish_read_fault; + vector = exception_vector(); + report(vector == PF_VECTOR, + "Expected #PF on VMREAD, got exception 0x%x", vector); - /* - * execute the vmread instruction that will cause a #PF - */ - handler_called = false; - asm volatile ("movb %[byte], %%ah\n\t" - "sahf\n\t" - "vmread %[enc], %[val]; finish_read_fault:" - : [val] "=m" (*(u64 *)vpage) - : [byte] "Krm" (sentinel), - [enc] "r" ((u64)GUEST_SEL_SS) - : "cc", "ah"); - report(handler_called, "The #PF handler was invoked"); - - /* - * restore the old #PF handler - */ - handle_exception(PF_VECTOR, old); + report((u8)flags == sentinel, + "Expected RFLAGS 0x%x, got 0x%x", sentinel, (u8)flags); } static void test_vmread_flags_touch(void) { /* - * set up the sentinel value in the flags register. we - * choose these two values because they candy-stripe - * the 5 flags that sahf sets. + * Test with two values to candy-stripe the 5 flags stored/loaded by + * SAHF/LAHF. */ - sentinel = 0x91; - test_read_sentinel(); - - sentinel = 0x45; - test_read_sentinel(); + test_read_sentinel(0x91); + test_read_sentinel(0x45); } -static noinline void test_write_sentinel(void) +static void test_write_sentinel(u8 sentinel) { - void *vpage; + unsigned long flags = sentinel; + unsigned int vector; struct vmcs *vmcs; - handler old; + void *vpage; - prep_flags_test_env(&vpage, &vmcs, &old); + prep_flags_test_env(&vpage, &vmcs); /* - * set the proper label + * Execute VMWRITE with a not-PRESENT memory operand, and verify a #PF + * occurred and RFLAGS were not modified. */ - extern char finish_write_fault; + asm volatile ("sahf\n\t" + ASM_TRY("1f") + "vmwrite %[val], %[enc]\n\t" + "1: lahf" + : [val] "=m" (*(u64 *)vpage), + [flags] "+a" (flags) + : [enc] "r" ((u64)GUEST_SEL_SS) + : "cc"); - finish_fault = (ulong)&finish_write_fault; + vector = exception_vector(); + report(vector == PF_VECTOR, + "Expected #PF on VMWRITE, got exception '0x%x'\n", vector); - /* - * execute the vmwrite instruction that will cause a #PF - */ - handler_called = false; - asm volatile ("movb %[byte], %%ah\n\t" - "sahf\n\t" - "vmwrite %[val], %[enc]; finish_write_fault:" - : [val] "=m" (*(u64 *)vpage) - : [byte] "Krm" (sentinel), - [enc] "r" ((u64)GUEST_SEL_SS) - : "cc", "ah"); - report(handler_called, "The #PF handler was invoked"); - - /* - * restore the old #PF handler - */ - handle_exception(PF_VECTOR, old); + report((u8)flags == sentinel, + "Expected RFLAGS 0x%x, got 0x%x", sentinel, (u8)flags); } static void test_vmwrite_flags_touch(void) { /* - * set up the sentinel value in the flags register. we - * choose these two values because they candy-stripe - * the 5 flags that sahf sets. + * Test with two values to candy-stripe the 5 flags stored/loaded by + * SAHF/LAHF. */ - sentinel = 0x91; - test_write_sentinel(); - - sentinel = 0x45; - test_write_sentinel(); + test_write_sentinel(0x91); + test_write_sentinel(0x45); } - static void test_vmcs_high(void) { struct vmcs *vmcs = alloc_page(); -- 2.38.0.rc1.362.ged0d419d3c-goog