diff mbox series

[kvm-unit-tests,v2,3/4] x86/access: Forced emulation support

Message ID 20230331135709.132713-4-minipli@grsecurity.net (mailing list archive)
State New, archived
Headers show
Series Tests for CR0.WP=0/1 r/o write access | expand

Commit Message

Mathias Krause March 31, 2023, 1:57 p.m. UTC
Add support to enforce access tests to be handled by the emulator, if
supported by KVM. Exclude it from the ac_test_exec() test, though, to
not slow it down too much.

Signed-off-by: Mathias Krause <minipli@grsecurity.net>
---
 x86/access.c | 37 ++++++++++++++++++++++++++++++++-----
 1 file changed, 32 insertions(+), 5 deletions(-)

Comments

Sean Christopherson March 31, 2023, 4:24 p.m. UTC | #1
On Fri, Mar 31, 2023, Mathias Krause wrote:
> Add support to enforce access tests to be handled by the emulator, if
> supported by KVM. Exclude it from the ac_test_exec() test, though, to
> not slow it down too much.

/enthusiastic high five

I was going to ask if you could extend the test to utilize FEP, and woke up this
morning to see it already done.  Thanks!!!!!

> Signed-off-by: Mathias Krause <minipli@grsecurity.net>
> ---

...

> @@ -152,6 +161,7 @@ const char *ac_names[] = {
>  	[AC_CPU_CR0_WP_BIT] = "cr0.wp",
>  	[AC_CPU_CR4_SMEP_BIT] = "cr4.smep",
>  	[AC_CPU_CR4_PKE_BIT] = "cr4.pke",
> +	[AC_FEP_BIT] = "fep",
>  };
>  
>  static inline void *va(pt_element_t phys)
> @@ -190,6 +200,7 @@ typedef struct {
>  
>  static void ac_test_show(ac_test_t *at);
>  
> +static bool fep_available;

I don't think fep_available needs to be captured in a global bool, the usage in
the CR0_WP test can do

	if (invalid_mask & AC_FEP_MASK)
		<skip>
Mathias Krause April 3, 2023, 9:08 a.m. UTC | #2
On 31.03.23 18:24, Sean Christopherson wrote:
> On Fri, Mar 31, 2023, Mathias Krause wrote:
>> Add support to enforce access tests to be handled by the emulator, if
>> supported by KVM. Exclude it from the ac_test_exec() test, though, to
>> not slow it down too much.
> 
> /enthusiastic high five

*clap*

> 
> I was going to ask if you could extend the test to utilize FEP, and woke up this
> morning to see it already done.  Thanks!!!!!
> 
>> Signed-off-by: Mathias Krause <minipli@grsecurity.net>
>> ---
> 
> ...
> 
>> @@ -152,6 +161,7 @@ const char *ac_names[] = {
>>  	[AC_CPU_CR0_WP_BIT] = "cr0.wp",
>>  	[AC_CPU_CR4_SMEP_BIT] = "cr4.smep",
>>  	[AC_CPU_CR4_PKE_BIT] = "cr4.pke",
>> +	[AC_FEP_BIT] = "fep",
>>  };
>>  
>>  static inline void *va(pt_element_t phys)
>> @@ -190,6 +200,7 @@ typedef struct {
>>  
>>  static void ac_test_show(ac_test_t *at);
>>  
>> +static bool fep_available;
> 
> I don't think fep_available needs to be captured in a global bool, the usage in
> the CR0_WP test can do
> 
> 	if (invalid_mask & AC_FEP_MASK)
> 		<skip>

Ok, makes it a little uglier, IMHO, but will change.

Again, I was just looking at other tests and copied what I thought was
sensible. But looking at x86/emulator.c again, I see it's not consistent
either. It caches the value in test_mov_pop_ss_code_db() but does not in
main().

Maybe I should just use is_fep_available() twice as well, as the
additional exception handling shouldn't matter much.

Thanks,
Mathias
diff mbox series

Patch

diff --git a/x86/access.c b/x86/access.c
index d1ec99b4fa73..ae5e7d8e8892 100644
--- a/x86/access.c
+++ b/x86/access.c
@@ -82,6 +82,13 @@  enum {
 	AC_CPU_CR4_SMEP_BIT,
 	AC_CPU_CR4_PKE_BIT,
 
+	NR_AC_TEST_FLAGS,
+
+	/*
+	 *  synthetic flags follow, won't be exercised by ac_test_exec().
+	 */
+	AC_FEP_BIT,
+
 	NR_AC_FLAGS
 };
 
@@ -121,6 +128,8 @@  enum {
 #define AC_CPU_CR4_SMEP_MASK  (1 << AC_CPU_CR4_SMEP_BIT)
 #define AC_CPU_CR4_PKE_MASK   (1 << AC_CPU_CR4_PKE_BIT)
 
+#define AC_FEP_MASK           (1 << AC_FEP_BIT)
+
 const char *ac_names[] = {
 	[AC_PTE_PRESENT_BIT] = "pte.p",
 	[AC_PTE_ACCESSED_BIT] = "pte.a",
@@ -152,6 +161,7 @@  const char *ac_names[] = {
 	[AC_CPU_CR0_WP_BIT] = "cr0.wp",
 	[AC_CPU_CR4_SMEP_BIT] = "cr4.smep",
 	[AC_CPU_CR4_PKE_BIT] = "cr4.pke",
+	[AC_FEP_BIT] = "fep",
 };
 
 static inline void *va(pt_element_t phys)
@@ -190,6 +200,7 @@  typedef struct {
 
 static void ac_test_show(ac_test_t *at);
 
+static bool fep_available;
 static unsigned long shadow_cr0;
 static unsigned long shadow_cr3;
 static unsigned long shadow_cr4;
@@ -396,7 +407,7 @@  static void ac_test_init(ac_test_t *at, unsigned long virt, ac_pt_env_t *pt_env)
 static int ac_test_bump_one(ac_test_t *at)
 {
 	at->flags = ((at->flags | invalid_mask) + 1) & ~invalid_mask;
-	return at->flags < (1 << NR_AC_FLAGS);
+	return at->flags < (1 << NR_AC_TEST_FLAGS);
 }
 
 #define F(x)  ((flags & x##_MASK) != 0)
@@ -799,10 +810,13 @@  static int ac_test_do_access(ac_test_t *at)
 
 	if (F(AC_ACCESS_TWICE)) {
 		asm volatile ("mov $fixed2, %%rsi \n\t"
-			      "mov (%[addr]), %[reg] \n\t"
+			      "cmp $0, %[fep] \n\t"
+			      "jz 1f \n\t"
+			      KVM_FEP
+			      "1: mov (%[addr]), %[reg] \n\t"
 			      "fixed2:"
 			      : [reg]"=r"(r), [fault]"=a"(fault), "=b"(e)
-			      : [addr]"r"(at->virt)
+			      : [addr]"r"(at->virt), [fep]"r"(F(AC_FEP))
 			      : "rsi");
 		fault = 0;
 	}
@@ -823,9 +837,15 @@  static int ac_test_do_access(ac_test_t *at)
 		      "jnz 2f \n\t"
 		      "cmp $0, %[write] \n\t"
 		      "jnz 1f \n\t"
-		      "mov (%[addr]), %[reg] \n\t"
+		      "cmp $0, %[fep] \n\t"
+		      "jz 0f \n\t"
+		      KVM_FEP
+		      "0: mov (%[addr]), %[reg] \n\t"
 		      "jmp done \n\t"
-		      "1: mov %[reg], (%[addr]) \n\t"
+		      "1: cmp $0, %[fep] \n\t"
+		      "jz 0f \n\t"
+		      KVM_FEP
+		      "0: mov %[reg], (%[addr]) \n\t"
 		      "jmp done \n\t"
 		      "2: call *%[addr] \n\t"
 		      "done: \n"
@@ -843,6 +863,7 @@  static int ac_test_do_access(ac_test_t *at)
 			[write]"r"(F(AC_ACCESS_WRITE)),
 			[user]"r"(F(AC_ACCESS_USER)),
 			[fetch]"r"(F(AC_ACCESS_FETCH)),
+			[fep]"r"(F(AC_FEP)),
 			[user_ds]"i"(USER_DS),
 			[user_cs]"i"(USER_CS),
 			[user_stack_top]"r"(user_stack + sizeof user_stack),
@@ -1228,6 +1249,12 @@  int ac_test_run(int pt_levels)
 		invalid_mask |= AC_PTE_BIT36_MASK;
 	}
 
+	fep_available = is_fep_available();
+	if (!fep_available) {
+		printf("FEP not available, skipping emulation tests\n");
+		invalid_mask |= AC_FEP_MASK;
+	}
+
 	ac_env_int(&pt_env, pt_levels);
 	ac_test_init(&at, 0xffff923400000000ul, &pt_env);