[for_v23,15/16] selftests/x86/sgx: Add sub-test for exception behavior with exit handler
diff mbox series

Message ID 20191008044613.12350-16-sean.j.christopherson@intel.com
State New
Headers show
Series
  • x86/vdso: sgx: Major vDSO cleanup
Related show

Commit Message

Sean Christopherson Oct. 8, 2019, 4:46 a.m. UTC
Add a test to verify the kernel and vDSO provide the correct exception
info when using an exit handler, e.g. leaf, trapnr and error_code, and
that the vDSO correctly interprets the return from the exit handler.  To
do so, change the enclave's protections to read-only and iteratively fix
the faults encountered, with various assertions along the way, e.g. the
first fault should always be a !writable fault on the TCS, at least
three total faults should occur, etc...

Suggested-by: Cedric Xing <cedric.xing@intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
---
 tools/testing/selftests/x86/sgx/defines.h |  2 +
 tools/testing/selftests/x86/sgx/main.c    | 87 ++++++++++++++++++++++-
 2 files changed, 87 insertions(+), 2 deletions(-)

Patch
diff mbox series

diff --git a/tools/testing/selftests/x86/sgx/defines.h b/tools/testing/selftests/x86/sgx/defines.h
index ab9671b8a993..199a830e198a 100644
--- a/tools/testing/selftests/x86/sgx/defines.h
+++ b/tools/testing/selftests/x86/sgx/defines.h
@@ -37,7 +37,9 @@  typedef uint64_t u64;
 #include "../../../../../arch/x86/include/uapi/asm/sgx.h"
 
 #define ENCLU_EENTER	2
+#define ENCLU_ERESUME	3
 
 #define GP_VECTOR 13
+#define PF_VECTOR 14
 
 #endif /* TYPES_H */
diff --git a/tools/testing/selftests/x86/sgx/main.c b/tools/testing/selftests/x86/sgx/main.c
index ae1822b10c6f..8c3f0cd41098 100644
--- a/tools/testing/selftests/x86/sgx/main.c
+++ b/tools/testing/selftests/x86/sgx/main.c
@@ -337,14 +337,58 @@  static int basic_exit_handler(long rdi, long rsi, long rdx, int ret,
 	return 0;
 }
 
+static int nr_page_faults;
+
+static int mprotect_exit_handler(long rdi, long rsi, long rdx, int ret,
+				 long r8, long r9, void *tcs, long ursp,
+				 struct sgx_enclave_exception *e)
+{
+	int prot, rc;
+
+	if (!ret)
+		return 0;
+
+	++nr_page_faults;
+
+	ASSERT_EQ(ret, -EFAULT);
+	ASSERT_EQ(e->trapnr, PF_VECTOR);
+	TEST_ASSERT(e->leaf == ENCLU_EENTER || e->leaf == ENCLU_ERESUME,
+		    "Expected #PF on EENTER or ERESUME, leaf = %d\n", e->leaf);
+	TEST_ASSERT(e->error_code & 1, "Unexpected !PRESENT #PF");
+
+	/* The first #PF should be on the TCS, passed in via R9. */
+	if (nr_page_faults == 1)
+		ASSERT_EQ(r9, (e->address & ~0xfff));
+
+	prot = PROT_READ;
+	if (e->error_code & 0x2)
+		prot |= PROT_WRITE;
+	if (e->error_code & 0x10)
+		prot |= PROT_EXEC;
+	rc = mprotect((void *)(e->address & ~0xfff), PAGE_SIZE, prot);
+	ASSERT_EQ(rc, 0);
+
+	/*
+	 * If EENTER faulted, bounce all the way back to the test to verify
+	 * the vDSO is handling the return value correctly.
+	 */
+	if (e->leaf == ENCLU_EENTER)
+		return -EAGAIN;
+
+	/* Else ERESUME faulted, simply do ERESUME again. */
+	return e->leaf;
+}
+
 /*
  * Test the vDSO API, __vdso_sgx_enter_enclave(), with an exit handler.
  */
-static void test_vdso_with_exit_handler(struct sgx_secs *secs)
+static void test_vdso_with_exit_handler(struct sgx_secs *secs,
+					unsigned long encl_size)
 {
 	struct sgx_enclave_exception exception;
 	uint64_t result = 0;
 	long ret;
+	int r;
 
 	memset(&exception, 0, sizeof(exception));
 
@@ -352,6 +396,45 @@  static void test_vdso_with_exit_handler(struct sgx_secs *secs)
 		       &exception, basic_exit_handler);
 	ASSERT_EQ(ret, 0);
 	ASSERT_EQ(result, MAGIC);
+
+	/*
+	 * Map the enclave read-only, then re-enter the enclave.  The exit
+	 * handler will service the resulting page faults using mprotect() to
+	 * restore the correct permissions.
+	 */
+	r = mprotect((void *)secs->base, encl_size, PROT_READ);
+	TEST_ASSERT(!r, "mprotect() on enclave failed: %s\n", strerror(errno));
+
+
+	/* Loop on EENTER until it succeeds or it fails unexpectedly. */
+	result = 0;
+	do {
+		/*
+		 * Pass the address of the TCS to the exit handler via R9.
+		 * The first page fault should be on the TCS and R9 should
+		 * not be modified prior to entering the enclave (which
+		 * requires an accessible TCS page).
+		 */
+		ret = sgx_call((void *)&MAGIC, &result, 0, 0, 0, secs->base,
+			       (void *)secs->base, &exception,
+			       mprotect_exit_handler);
+	} while (ret == -EAGAIN);
+	ASSERT_EQ(ret, 0);
+	ASSERT_EQ(result, MAGIC);
+
+	/* Enclave should re-execute cleanly. */
+	result = 0;
+	ret = sgx_call((void *)&MAGIC, &result, 0, 0, 0, 0, (void *)secs->base,
+		       &exception, basic_exit_handler);
+	ASSERT_EQ(ret, 0);
+	ASSERT_EQ(result, MAGIC);
+
+	/*
+	 * At least three faults should occur: one for the TCS, one for the
+	 * executable code, and one for the writable data (@result).
+	 */
+	TEST_ASSERT(nr_page_faults >= 3, "Expected 3+ page faults, only hit %d",
+		    nr_page_faults);
 }
 
 int main(int argc, char *argv[], char *envp[])
@@ -381,7 +464,7 @@  int main(int argc, char *argv[], char *envp[])
 	encl_build(&secs, bin, bin_size, &sigstruct);
 
 	test_vdso_no_exit_handler(&secs);
-	test_vdso_with_exit_handler(&secs);
+	test_vdso_with_exit_handler(&secs, bin_size);
 
 	printf("All tests passed!\n");
 	exit(0);