@@ -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 */
@@ -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);
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(-)