x86: nVMX: add new test for vmread/vmwrite flags preservation
PATCH 1/1 x86: nVMX: add new test for vmread/vmwrite flags preservation
This commit adds new unit tests for commit a4d956b93904 ("KVM: nVMX:
vmread should not set rflags to specify success in case of #PF")
The two new tests force a vmread and a vmwrite on an unmapped address to cause a #PF and verify that the low byte of %rflags is preserved and that %rip is not advanced. The commit fixed a bug in vmread, but we include a test for vmwrite as well for completeness.
Before the aforementioned commit, the ALU flags would be incorrectly cleared and %rip would be advanced (for vmread).
v1: https://www.spinics.net/lists/kvm/msg212817.html
--- x86/vmx.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+)
x86/vmx.c +121 -0
@@ -32,6 +32,7 @@#include "processor.h" #include "alloc_page.h" #include "vm.h" +#include "vmalloc.h" #include "desc.h" #include "vmx.h" #include "msr.h"
@@ -368,6 +369,122 @@ 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 ("RIP has not been advanced!" , + regs->rip < finish_fault); +report ("The low byte of RFLAGS was preserved!" , + ((u8 )regs->rflags == ((sentinel |2 ) &0xd7 ))); + + regs->rip = finish_fault; + handler_called = true; + +} + +static void prep_flags_test_env (void **vpage,struct vmcs **vmcs,handler *old) +{ +// get an unbacked address that will cause a #PF + *vpage =alloc_vpage (); + +// set up VMCS so we have something to read from + *vmcs =alloc_page (); + +memset (*vmcs,0 ,PAGE_SIZE ); + (*vmcs)->hdr.revision_id = basic.revision; +assert (!vmcs_clear (*vmcs)); +assert (!make_vmcs_current (*vmcs)); + + *old =handle_exception (PF_VECTOR , &pf_handler); +} + +static void test_read_sentinel (void ) +{ +void *vpage; +struct vmcs *vmcs; +handler old; + +prep_flags_test_env (&vpage, &vmcs, &old); + +// set the proper label +extern char finish_read_fault; + + finish_fault = (ulong)&finish_read_fault; + +// execute the vmread instruction that will cause a #PF + handler_called = false; + asmvolatile ("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 ("The #PF handler was invoked" , handler_called); + +// restore old #PF handler +handle_exception (PF_VECTOR , old); +} + +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. + sentinel =0x91 ; +test_read_sentinel (); + + sentinel =0x45 ; +test_read_sentinel (); +} + +static void test_write_sentinel (void ) +{ +void *vpage; +struct vmcs *vmcs; +handler old; + +prep_flags_test_env (&vpage, &vmcs, &old); + +// set the proper label +extern char finish_write_fault; + + finish_fault = (ulong)&finish_write_fault; + +// execute the vmwrite instruction that will cause a #PF + handler_called = false; + asmvolatile ("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 ("The #PF handler was invoked" , handler_called); + +// restore old #PF handler +handle_exception (PF_VECTOR , old); +} + +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. + sentinel =0x91 ; +test_write_sentinel (); + + sentinel =0x45 ; +test_write_sentinel (); +} + +static void test_vmcs_high (void ) {struct vmcs *vmcs =alloc_page ();
@@ -1994,6 +2111,10 @@ int main(int argc, const char *argv[])test_vmcs_lifecycle ();if (test_wanted ("test_vmx_caps" , argv, argc))test_vmx_caps (); +if (test_wanted ("test_vmread_flags_touch" , argv, argc)) +test_vmread_flags_touch (); +if (test_wanted ("test_vmwrite_flags_touch" , argv, argc)) +test_vmwrite_flags_touch ();/* Balance vmxon from test_vmxon. */ vmx_off ();
Signature · 1 line
2.26.0.110.g2183baf09c-goog
Hi Simon,
Quoted 36 lines · Simon Smith
This commit adds new unit tests for commit
a4d956b93904("KVM: nVMX:
vmread should not set rflags to specify success in case of #PF")The two new tests force a vmread and a vmwrite on an unmapped address to cause a #PF and verify that the low byte of %rflags is preserved and that %rip is not advanced. The commit fixed a bug in vmread, but we include a test for vmwrite as well for completeness.
Before the aforementioned commit, the ALU flags would be incorrectly cleared and %rip would be advanced (for vmread).
v1: https://www.spinics.net/lists/kvm/msg212817.html
Reviewed-by Jim Mattson <[email protected]>Reviewed-by Peter Shier <[email protected]>Reviewed-by Krish Sadhukhan <[email protected]>Signed-off-by Simon Smith <[email protected]>--- x86/vmx.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+)x86/vmx.c +1
-0@@ -32,6 +32,7 @@#include "processor.h" #include "alloc_page.h" #include "vm.h" +#include "vmalloc.h" #include "desc.h" #include "vmx.h" #include "msr.h"
x86/vmx.c static void test_vmwrite_vmread(void) · line 374
Quoted 6 lines
x86/vmx.c +3
-0@@ -368,6 +369,122 @@ static void test_vmwrite_vmread(void)free_page (vmcs); } +ulong finish_fault; +u8 sentinel; +bool handler_called;
nit: newline here
x86/vmx.c static void test_vmwrite_vmread(void) · line 378
Quoted 4 lines
x86/vmx.c +4
-0+static void pf_handler (struct ex_regs *regs) +{ +// check that RIP was not improperly advanced and that the +// flags value was preserved.
Throughout the patch, could you use C/kernel style comments?
/*
* check that RIP was not improperly advanced and that the
* flags value was perserved.
*/
This should also be used for single line comments.
x86/vmx.c static void test_vmwrite_vmread(void) · line 382
Quoted 4 lines
x86/vmx.c +4
-0+report ("RIP has not been advanced!" , + regs->rip < finish_fault); +report ("The low byte of RFLAGS was preserved!" , + ((u8 )regs->rflags == ((sentinel |2 ) &0xd7 )));
This doesn't compile. The signature of report is:
extern void report(bool pass, const char *msg_fmt, ...)
However, this is completely understandable, as report(...) used to be parameterized the way you have it :)
Quoted 36 lines
x86/vmx.c +36
-0+ + regs->rip = finish_fault; + handler_called = true; + +} + +static void prep_flags_test_env (void **vpage,struct vmcs **vmcs,handler *old) +{ +// get an unbacked address that will cause a #PF + *vpage =alloc_vpage (); + +// set up VMCS so we have something to read from + *vmcs =alloc_page (); + +memset (*vmcs,0 ,PAGE_SIZE ); + (*vmcs)->hdr.revision_id = basic.revision; +assert (!vmcs_clear (*vmcs)); +assert (!make_vmcs_current (*vmcs)); + + *old =handle_exception (PF_VECTOR , &pf_handler); +} + +static void test_read_sentinel (void ) +{ +void *vpage; +struct vmcs *vmcs; +handler old; + +prep_flags_test_env (&vpage, &vmcs, &old); + +// set the proper label +extern char finish_read_fault; + + finish_fault = (ulong)&finish_read_fault; + +// execute the vmread instruction that will cause a #PF
x86/vmx.c static void test_vmwrite_vmread(void) · line 428
Quoted 10 lines
x86/vmx.c +10
-0+ handler_called = false; + asmvolatile ("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 ("The #PF handler was invoked" , handler_called);
Same thing here, you'll need to reorder the parameters.
Quoted 31 lines
x86/vmx.c +31
-0+ +// restore old #PF handler +handle_exception (PF_VECTOR , old); +} + +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. + sentinel =0x91 ; +test_read_sentinel (); + + sentinel =0x45 ; +test_read_sentinel (); +} + +static void test_write_sentinel (void ) +{ +void *vpage; +struct vmcs *vmcs; +handler old; + +prep_flags_test_env (&vpage, &vmcs, &old); + +// set the proper label +extern char finish_write_fault; + + finish_fault = (ulong)&finish_write_fault; + +// execute the vmwrite instruction that will cause a #PF
x86/vmx.c static void test_vmwrite_vmread(void) · line 469
Quoted 10 lines
x86/vmx.c +10
-0+ handler_called = false; + asmvolatile ("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 ("The #PF handler was invoked" , handler_called);
report(...) issue also here
Quoted 33 lines
x86/vmx.c +22
-1+ +// restore old #PF handler +handle_exception (PF_VECTOR , old); +} + +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. + sentinel =0x91 ; +test_write_sentinel (); + + sentinel =0x45 ; +test_write_sentinel (); +} + +static void test_vmcs_high (void ) {struct vmcs *vmcs =alloc_page ();@@ -1994,6 +2111,10 @@ int main(int argc, const char *argv[])test_vmcs_lifecycle ();if (test_wanted ("test_vmx_caps" , argv, argc))test_vmx_caps (); +if (test_wanted ("test_vmread_flags_touch" , argv, argc)) +test_vmread_flags_touch (); +if (test_wanted ("test_vmwrite_flags_touch" , argv, argc)) +test_vmwrite_flags_touch ();/* Balance vmxon from test_vmxon. */ vmx_off (); --
Quoted 2 lines
2.26.0.110.g2183baf09c-goog
--
Thanks,
Oliver