@@ -38,7 +38,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 /* DEFINES_H */
@@ -381,6 +381,95 @@ static void test_sgx_vdso_exit_handler(struct sgx_secs *secs)
ASSERT_EQ(result, MAGIC);
}
+static int nr_page_faults;
+
+static int mprotect_exit_handler(long rdi, long rsi, long rdx, long ursp,
+ long r8, long r9, void *tcs, int ret,
+ 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);
+ ASSERT_RAW(e->leaf == ENCLU_EENTER || e->leaf == ENCLU_ERESUME,
+ "Expected #PF on EENTER or ERESUME, leaf = %d\n", e->leaf);
+
+ /* The first #PF should be on the TCS, passed in via R9. */
+ if (nr_page_faults == 1) {
+ ASSERT_EQ(r9, (e->address & ~0xfff));
+ ASSERT_TRUE(e->error_code & 0x2);
+ }
+
+ 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;
+}
+
+static void test_sgx_vdso_exception_handler(struct sgx_secs *secs)
+{
+ struct sgx_enclave_exception exception;
+ uint64_t result = 0;
+ int ret;
+
+ memset(&exception, 0, sizeof(exception));
+
+ /*
+ * Make all pages inaccessible, then re-enter the enclave. The exit
+ * handler will service the resulting page faults using mprotect() to
+ * restore the correct permissions.
+ */
+ ret = mprotect((void *)secs->base, secs->size, PROT_NONE);
+ ASSERT_RAW(!ret, "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 (whic
+ * requires an accessible TCS page).
+ */
+ ret = sgx_call_vdso((void *)&MAGIC, &result, NULL, NULL, NULL,
+ (void *)secs->base, (void *)secs->base,
+ &exception, mprotect_exit_handler);
+ } while (ret == -EAGAIN);
+ EXPECT_EQ(ret, 0);
+ EXPECT_EQ(result, MAGIC);
+
+ /* Enclave should re-execute cleanly. */
+ result = 0;
+ ret = sgx_call_vdso((void *)&MAGIC, &result, NULL, NULL, NULL, NULL,
+ (void *)secs->base, &exception, basic_exit_handler);
+ EXPECT_EQ(ret, 0);
+ EXPECT_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).
+ */
+ EXPECT_GE(nr_page_faults, 3);
+}
+
int main(int argc, char *argv[], char *envp[])
{
struct sgx_sigstruct sigstruct;
@@ -389,7 +478,7 @@ int main(int argc, char *argv[], char *envp[])
void *bin;
ksft_print_header();
- ksft_set_plan(3);
+ ksft_set_plan(4);
bin = encl_data_map("encl.bin", &bin_size);
@@ -403,6 +492,7 @@ int main(int argc, char *argv[], char *envp[])
RUN_TEST(test_sgx_vdso);
RUN_TEST(test_sgx_vdso_exit_handler);
+ RUN_TEST(test_sgx_vdso_exception_handler);
return ksft_exit_pass();
}
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 PROT_NONE and iteratively fix the faults encountered, with various assertions along the way, e.g. the first fault should always be 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 | 92 ++++++++++++++++++++++- 2 files changed, 93 insertions(+), 1 deletion(-)