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

Mathias Krause
2023-03-31T13:57:35Z

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(-)

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);

--
2.39.2