summarylogtreecommitdiffstats
path: root/0017-selftests-sgx-Add-test-for-EPCM-permission-changes.patch
diff options
context:
space:
mode:
Diffstat (limited to '0017-selftests-sgx-Add-test-for-EPCM-permission-changes.patch')
-rw-r--r--0017-selftests-sgx-Add-test-for-EPCM-permission-changes.patch401
1 files changed, 401 insertions, 0 deletions
diff --git a/0017-selftests-sgx-Add-test-for-EPCM-permission-changes.patch b/0017-selftests-sgx-Add-test-for-EPCM-permission-changes.patch
new file mode 100644
index 000000000000..ea5c39188f31
--- /dev/null
+++ b/0017-selftests-sgx-Add-test-for-EPCM-permission-changes.patch
@@ -0,0 +1,401 @@
+From c6ad8031ecdc735fb70305c95be5a54c704a9ebc Mon Sep 17 00:00:00 2001
+From: Reinette Chatre <reinette.chatre@intel.com>
+Date: Mon, 7 Feb 2022 16:45:39 -0800
+Subject: [PATCH 17/34] selftests/sgx: Add test for EPCM permission changes
+
+EPCM permission changes could be made from within (to relax
+permissions) or out (to restrict permissions) the enclave. Kernel
+support is needed when permissions are restricted to be able to
+call the privileged ENCLS[EMODPR] instruction and ensure PTEs
+allowing the restricted permissions are flushed. EPCM permissions
+can be relaxed via ENCLU[EMODPE] from within the enclave but the
+enclave still depends on the kernel to install PTEs with the new
+permissions.
+
+Add a test that exercises a few of the enclave page permission flows:
+1) Test starts with a RW (from enclave and kernel perspective)
+ enclave page that is mapped via a RW VMA.
+2) Use the SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS ioctl() to restrict
+ the enclave (EPCM) page permissions to read-only (kernel removes
+ PTE in the process).
+3) Run ENCLU[EACCEPT] from within the enclave to accept the new page
+ permissions.
+4) Attempt to write to the enclave page from within the enclave - this
+ should fail with a page fault on the PTE since the page
+ table entry accurately reflects the (read-only) EPCM permissions.
+5) Restore EPCM permissions to RW by running ENCLU[EMODPE] from within
+ the enclave.
+6) Attempt to write to the enclave page from within the enclave - this
+ should fail again with a page fault because even though the EPCM
+ permissions are RW the PTE does not yet reflect that.
+7) Use the SGX_IOC_ENCLAVE_RELAX_PERMISSIONS ioctl() to inform the
+ kernel of new page permissions and PTEs will accurately reflect
+ RW EPCM permissions.
+8) Writing to enclave page from within enclave succeeds.
+
+Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
+---
+ tools/testing/selftests/sgx/defines.h | 15 ++
+ tools/testing/selftests/sgx/main.c | 253 ++++++++++++++++++++++++
+ tools/testing/selftests/sgx/test_encl.c | 38 ++++
+ 3 files changed, 306 insertions(+)
+
+diff --git a/tools/testing/selftests/sgx/defines.h b/tools/testing/selftests/sgx/defines.h
+index 02d775789ea7..b638eb98c80c 100644
+--- a/tools/testing/selftests/sgx/defines.h
++++ b/tools/testing/selftests/sgx/defines.h
+@@ -24,6 +24,8 @@ enum encl_op_type {
+ ENCL_OP_PUT_TO_ADDRESS,
+ ENCL_OP_GET_FROM_ADDRESS,
+ ENCL_OP_NOP,
++ ENCL_OP_EACCEPT,
++ ENCL_OP_EMODPE,
+ ENCL_OP_MAX,
+ };
+
+@@ -53,4 +55,17 @@ struct encl_op_get_from_addr {
+ uint64_t addr;
+ };
+
++struct encl_op_eaccept {
++ struct encl_op_header header;
++ uint64_t epc_addr;
++ uint64_t flags;
++ uint64_t ret;
++};
++
++struct encl_op_emodpe {
++ struct encl_op_header header;
++ uint64_t epc_addr;
++ uint64_t flags;
++};
++
+ #endif /* DEFINES_H */
+diff --git a/tools/testing/selftests/sgx/main.c b/tools/testing/selftests/sgx/main.c
+index dd74fa42302e..4f348ed1dc29 100644
+--- a/tools/testing/selftests/sgx/main.c
++++ b/tools/testing/selftests/sgx/main.c
+@@ -25,6 +25,18 @@ static const uint64_t MAGIC = 0x1122334455667788ULL;
+ static const uint64_t MAGIC2 = 0x8877665544332211ULL;
+ vdso_sgx_enter_enclave_t vdso_sgx_enter_enclave;
+
++/*
++ * Security Information (SECINFO) data structure needed by a few SGX
++ * instructions (eg. ENCLU[EACCEPT] and ENCLU[EMODPE]) holds meta-data
++ * about an enclave page. &enum sgx_secinfo_page_state specifies the
++ * secinfo flags used for page state.
++ */
++enum sgx_secinfo_page_state {
++ SGX_SECINFO_PENDING = (1 << 3),
++ SGX_SECINFO_MODIFIED = (1 << 4),
++ SGX_SECINFO_PR = (1 << 5),
++};
++
+ struct vdso_symtab {
+ Elf64_Sym *elf_symtab;
+ const char *elf_symstrtab;
+@@ -555,4 +567,245 @@ TEST_F(enclave, pte_permissions)
+ EXPECT_EQ(self->run.exception_addr, 0);
+ }
+
++/*
++ * Enclave page permission test.
++ *
++ * Modify and restore enclave page's EPCM (enclave) permissions from
++ * outside enclave (ENCLS[EMODPR] via kernel) as well as from within
++ * enclave (via ENCLU[EMODPE]). Kernel should ensure PTE permissions
++ * are the same as the EPCM permissions so check for page fault if
++ * VMA allows access but EPCM and PTE does not.
++ */
++TEST_F(enclave, epcm_permissions)
++{
++ struct sgx_enclave_restrict_perm restrict_ioc;
++ struct encl_op_get_from_addr get_addr_op;
++ struct sgx_enclave_relax_perm relax_ioc;
++ struct encl_op_put_to_addr put_addr_op;
++ struct encl_op_eaccept eaccept_op;
++ struct encl_op_emodpe emodpe_op;
++ struct sgx_secinfo secinfo;
++ unsigned long data_start;
++ int ret, errno_save;
++
++ ASSERT_TRUE(setup_test_encl(ENCL_HEAP_SIZE_DEFAULT, &self->encl, _metadata));
++
++ memset(&self->run, 0, sizeof(self->run));
++ self->run.tcs = self->encl.encl_base;
++
++ /*
++ * Ensure kernel supports needed ioctl() and system supports needed
++ * commands.
++ */
++ memset(&restrict_ioc, 0, sizeof(restrict_ioc));
++ memset(&secinfo, 0, sizeof(secinfo));
++
++ ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS,
++ &restrict_ioc);
++ errno_save = ret == -1 ? errno : 0;
++
++ /*
++ * Invalid parameters were provided during sanity check,
++ * expect command to fail.
++ */
++ ASSERT_EQ(ret, -1);
++
++ /* ret == -1 */
++ if (errno_save == ENOTTY)
++ SKIP(return,
++ "Kernel does not support SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS ioctl()");
++ else if (errno_save == ENODEV)
++ SKIP(return, "System does not support SGX2");
++
++ /*
++ * Page that will have its permissions changed is the second data
++ * page in the .data segment. This forms part of the local encl_buffer
++ * within the enclave.
++ *
++ * At start of test @data_start should have EPCM as well as PTE
++ * permissions of RW.
++ */
++
++ data_start = self->encl.encl_base +
++ encl_get_data_offset(&self->encl) + PAGE_SIZE;
++
++ /*
++ * Sanity check that page at @data_start is writable before making
++ * any changes to page permissions.
++ *
++ * Start by writing MAGIC to test page.
++ */
++ put_addr_op.value = MAGIC;
++ put_addr_op.addr = data_start;
++ put_addr_op.header.type = ENCL_OP_PUT_TO_ADDRESS;
++
++ EXPECT_EQ(ENCL_CALL(&put_addr_op, &self->run, true), 0);
++
++ EXPECT_EEXIT(&self->run);
++ EXPECT_EQ(self->run.exception_vector, 0);
++ EXPECT_EQ(self->run.exception_error_code, 0);
++ EXPECT_EQ(self->run.exception_addr, 0);
++
++ /*
++ * Read memory that was just written to, confirming that
++ * page is writable.
++ */
++ get_addr_op.value = 0;
++ get_addr_op.addr = data_start;
++ get_addr_op.header.type = ENCL_OP_GET_FROM_ADDRESS;
++
++ EXPECT_EQ(ENCL_CALL(&get_addr_op, &self->run, true), 0);
++
++ EXPECT_EQ(get_addr_op.value, MAGIC);
++ EXPECT_EEXIT(&self->run);
++ EXPECT_EQ(self->run.exception_vector, 0);
++ EXPECT_EQ(self->run.exception_error_code, 0);
++ EXPECT_EQ(self->run.exception_addr, 0);
++
++ /*
++ * Change EPCM permissions to read-only, PTE entry flushed by
++ * kernel in the process.
++ */
++ memset(&restrict_ioc, 0, sizeof(restrict_ioc));
++ memset(&secinfo, 0, sizeof(secinfo));
++
++ secinfo.flags = PROT_READ;
++ restrict_ioc.offset = encl_get_data_offset(&self->encl) + PAGE_SIZE;
++ restrict_ioc.length = PAGE_SIZE;
++ restrict_ioc.secinfo = (unsigned long)&secinfo;
++
++ ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS,
++ &restrict_ioc);
++ errno_save = ret == -1 ? errno : 0;
++
++ EXPECT_EQ(ret, 0);
++ EXPECT_EQ(errno_save, 0);
++ EXPECT_EQ(restrict_ioc.result, 0);
++ EXPECT_EQ(restrict_ioc.count, 4096);
++
++ /*
++ * EPCM permissions changed from kernel, need to EACCEPT from enclave.
++ */
++ eaccept_op.epc_addr = data_start;
++ eaccept_op.flags = PROT_READ | SGX_SECINFO_REG | SGX_SECINFO_PR;
++ eaccept_op.ret = 0;
++ eaccept_op.header.type = ENCL_OP_EACCEPT;
++
++ EXPECT_EQ(ENCL_CALL(&eaccept_op, &self->run, true), 0);
++
++ EXPECT_EEXIT(&self->run);
++ EXPECT_EQ(self->run.exception_vector, 0);
++ EXPECT_EQ(self->run.exception_error_code, 0);
++ EXPECT_EQ(self->run.exception_addr, 0);
++ EXPECT_EQ(eaccept_op.ret, 0);
++
++ /*
++ * EPCM permissions of page is now read-only, expect #PF
++ * on PTE (not EPCM) when attempting to write to page from
++ * within enclave.
++ */
++ put_addr_op.value = MAGIC2;
++
++ EXPECT_EQ(ENCL_CALL(&put_addr_op, &self->run, true), 0);
++
++ EXPECT_EQ(self->run.function, ERESUME);
++ EXPECT_EQ(self->run.exception_vector, 14);
++ EXPECT_EQ(self->run.exception_error_code, 0x7);
++ EXPECT_EQ(self->run.exception_addr, data_start);
++
++ self->run.exception_vector = 0;
++ self->run.exception_error_code = 0;
++ self->run.exception_addr = 0;
++
++ /*
++ * Received AEX but cannot return to enclave at same entrypoint,
++ * need different TCS from where EPCM permission can be made writable
++ * again.
++ */
++ self->run.tcs = self->encl.encl_base + PAGE_SIZE;
++
++ /*
++ * Enter enclave at new TCS to change EPCM permissions to be
++ * writable again and thus fix the page fault that triggered the
++ * AEX.
++ */
++
++ emodpe_op.epc_addr = data_start;
++ emodpe_op.flags = PROT_READ | PROT_WRITE;
++ emodpe_op.header.type = ENCL_OP_EMODPE;
++
++ EXPECT_EQ(ENCL_CALL(&emodpe_op, &self->run, true), 0);
++
++ EXPECT_EEXIT(&self->run);
++ EXPECT_EQ(self->run.exception_vector, 0);
++ EXPECT_EQ(self->run.exception_error_code, 0);
++ EXPECT_EQ(self->run.exception_addr, 0);
++
++ /*
++ * Attempt to return to main TCS to resume execution at faulting
++ * instruction, but PTE should still prevent writing to the page.
++ */
++ self->run.tcs = self->encl.encl_base;
++
++ EXPECT_EQ(vdso_sgx_enter_enclave((unsigned long)&put_addr_op, 0, 0,
++ ERESUME, 0, 0,
++ &self->run),
++ 0);
++
++ EXPECT_EQ(self->run.function, ERESUME);
++ EXPECT_EQ(self->run.exception_vector, 14);
++ EXPECT_EQ(self->run.exception_error_code, 0x7);
++ EXPECT_EQ(self->run.exception_addr, data_start);
++
++ self->run.exception_vector = 0;
++ self->run.exception_error_code = 0;
++ self->run.exception_addr = 0;
++ /*
++ * Inform kernel about new permissions to have PTEs match EPCM.
++ */
++ memset(&relax_ioc, 0, sizeof(relax_ioc));
++ memset(&secinfo, 0, sizeof(secinfo));
++
++ secinfo.flags = PROT_READ | PROT_WRITE;
++ relax_ioc.offset = encl_get_data_offset(&self->encl) + PAGE_SIZE;
++ relax_ioc.length = PAGE_SIZE;
++ relax_ioc.secinfo = (unsigned long)&secinfo;
++
++ ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_RELAX_PERMISSIONS,
++ &relax_ioc);
++ errno_save = ret == -1 ? errno : 0;
++
++ EXPECT_EQ(ret, 0);
++ EXPECT_EQ(errno_save, 0);
++ EXPECT_EQ(relax_ioc.count, 4096);
++
++ /*
++ * Wrong page permissions that caused original fault has
++ * now been fixed via EPCM permissions as well as PTE.
++ * Resume execution in main TCS to re-attempt the memory access.
++ */
++ self->run.tcs = self->encl.encl_base;
++
++ EXPECT_EQ(vdso_sgx_enter_enclave((unsigned long)&put_addr_op, 0, 0,
++ ERESUME, 0, 0,
++ &self->run),
++ 0);
++
++ EXPECT_EEXIT(&self->run);
++ EXPECT_EQ(self->run.exception_vector, 0);
++ EXPECT_EQ(self->run.exception_error_code, 0);
++ EXPECT_EQ(self->run.exception_addr, 0);
++
++ get_addr_op.value = 0;
++
++ EXPECT_EQ(ENCL_CALL(&get_addr_op, &self->run, true), 0);
++
++ EXPECT_EQ(get_addr_op.value, MAGIC2);
++ EXPECT_EEXIT(&self->run);
++ EXPECT_EQ(self->run.user_data, 0);
++ EXPECT_EQ(self->run.exception_vector, 0);
++ EXPECT_EQ(self->run.exception_error_code, 0);
++ EXPECT_EQ(self->run.exception_addr, 0);
++}
++
+ TEST_HARNESS_MAIN
+diff --git a/tools/testing/selftests/sgx/test_encl.c b/tools/testing/selftests/sgx/test_encl.c
+index 4fca01cfd898..5b6c65331527 100644
+--- a/tools/testing/selftests/sgx/test_encl.c
++++ b/tools/testing/selftests/sgx/test_encl.c
+@@ -11,6 +11,42 @@
+ */
+ static uint8_t encl_buffer[8192] = { 1 };
+
++enum sgx_enclu_function {
++ EACCEPT = 0x5,
++ EMODPE = 0x6,
++};
++
++static void do_encl_emodpe(void *_op)
++{
++ struct sgx_secinfo secinfo __aligned(sizeof(struct sgx_secinfo)) = {0};
++ struct encl_op_emodpe *op = _op;
++
++ secinfo.flags = op->flags;
++
++ asm volatile(".byte 0x0f, 0x01, 0xd7"
++ :
++ : "a" (EMODPE),
++ "b" (&secinfo),
++ "c" (op->epc_addr));
++}
++
++static void do_encl_eaccept(void *_op)
++{
++ struct sgx_secinfo secinfo __aligned(sizeof(struct sgx_secinfo)) = {0};
++ struct encl_op_eaccept *op = _op;
++ int rax;
++
++ secinfo.flags = op->flags;
++
++ asm volatile(".byte 0x0f, 0x01, 0xd7"
++ : "=a" (rax)
++ : "a" (EACCEPT),
++ "b" (&secinfo),
++ "c" (op->epc_addr));
++
++ op->ret = rax;
++}
++
+ static void *memcpy(void *dest, const void *src, size_t n)
+ {
+ size_t i;
+@@ -62,6 +98,8 @@ void encl_body(void *rdi, void *rsi)
+ do_encl_op_put_to_addr,
+ do_encl_op_get_from_addr,
+ do_encl_op_nop,
++ do_encl_eaccept,
++ do_encl_emodpe,
+ };
+
+ struct encl_op_header *op = (struct encl_op_header *)rdi;
+--
+2.35.1
+