selftests/sgx: Test two different SGX2 EAUG flows
commit67f1f70a23
upstream. Enclave pages can be added to an initialized enclave when an address belonging to the enclave but without a backing page is accessed from within the enclave. Accessing memory without a backing enclave page from within an enclave can be in different ways: 1) Pre-emptively run ENCLU[EACCEPT]. Since the addition of a page always needs to be accepted by the enclave via ENCLU[EACCEPT] this flow is efficient since the first execution of ENCLU[EACCEPT] triggers the addition of the page and when execution returns to the same instruction the second execution would be successful as an acceptance of the page. 2) A direct read or write. The flow where a direct read or write triggers the page addition execution cannot resume from the instruction (read/write) that triggered the fault but instead the enclave needs to be entered at a different entry point to run needed ENCLU[EACCEPT] before execution can return to the original entry point and the read/write instruction that faulted. Add tests for both flows. Intel-SIG: commit67f1f70a23
selftests/sgx: Test two different SGX2 EAUG flows. Backport for SGX EDMM support. Signed-off-by: Reinette Chatre <reinette.chatre@intel.com> Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com> Acked-by: Jarkko Sakkinen <jarkko@kernel.org> Link: https://lkml.kernel.org/r/0c321e0e32790ac1de742ce5017a331e6d902ac1.1652137848.git.reinette.chatre@intel.com [ Zhiquan: amend commit log ] Signed-off-by: Zhiquan Li <zhiquan1.li@intel.com>
This commit is contained in:
parent
09fa1ee6fe
commit
6c6b3e0aa9
|
@ -86,6 +86,15 @@ static bool vdso_get_symtab(void *addr, struct vdso_symtab *symtab)
|
|||
return true;
|
||||
}
|
||||
|
||||
static inline int sgx2_supported(void)
|
||||
{
|
||||
unsigned int eax, ebx, ecx, edx;
|
||||
|
||||
__cpuid_count(SGX_CPUID, 0x0, eax, ebx, ecx, edx);
|
||||
|
||||
return eax & 0x2;
|
||||
}
|
||||
|
||||
static unsigned long elf_sym_hash(const char *name)
|
||||
{
|
||||
unsigned long h = 0, high;
|
||||
|
@ -839,4 +848,245 @@ TEST_F(enclave, epcm_permissions)
|
|||
EXPECT_EQ(self->run.exception_addr, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test the addition of pages to an initialized enclave via writing to
|
||||
* a page belonging to the enclave's address space but was not added
|
||||
* during enclave creation.
|
||||
*/
|
||||
TEST_F(enclave, augment)
|
||||
{
|
||||
struct encl_op_get_from_addr get_addr_op;
|
||||
struct encl_op_put_to_addr put_addr_op;
|
||||
struct encl_op_eaccept eaccept_op;
|
||||
size_t total_size = 0;
|
||||
void *addr;
|
||||
int i;
|
||||
|
||||
if (!sgx2_supported())
|
||||
SKIP(return, "SGX2 not supported");
|
||||
|
||||
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;
|
||||
|
||||
for (i = 0; i < self->encl.nr_segments; i++) {
|
||||
struct encl_segment *seg = &self->encl.segment_tbl[i];
|
||||
|
||||
total_size += seg->size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Actual enclave size is expected to be larger than the loaded
|
||||
* test enclave since enclave size must be a power of 2 in bytes
|
||||
* and test_encl does not consume it all.
|
||||
*/
|
||||
EXPECT_LT(total_size + PAGE_SIZE, self->encl.encl_size);
|
||||
|
||||
/*
|
||||
* Create memory mapping for the page that will be added. New
|
||||
* memory mapping is for one page right after all existing
|
||||
* mappings.
|
||||
* Kernel will allow new mapping using any permissions if it
|
||||
* falls into the enclave's address range but not backed
|
||||
* by existing enclave pages.
|
||||
*/
|
||||
addr = mmap((void *)self->encl.encl_base + total_size, PAGE_SIZE,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_SHARED | MAP_FIXED, self->encl.fd, 0);
|
||||
EXPECT_NE(addr, MAP_FAILED);
|
||||
|
||||
self->run.exception_vector = 0;
|
||||
self->run.exception_error_code = 0;
|
||||
self->run.exception_addr = 0;
|
||||
|
||||
/*
|
||||
* Attempt to write to the new page from within enclave.
|
||||
* Expected to fail since page is not (yet) part of the enclave.
|
||||
* The first #PF will trigger the addition of the page to the
|
||||
* enclave, but since the new page needs an EACCEPT from within the
|
||||
* enclave before it can be used it would not be possible
|
||||
* to successfully return to the failing instruction. This is the
|
||||
* cause of the second #PF captured here having the SGX bit set,
|
||||
* it is from hardware preventing the page from being used.
|
||||
*/
|
||||
put_addr_op.value = MAGIC;
|
||||
put_addr_op.addr = (unsigned long)addr;
|
||||
put_addr_op.header.type = ENCL_OP_PUT_TO_ADDRESS;
|
||||
|
||||
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_addr, (unsigned long)addr);
|
||||
|
||||
if (self->run.exception_error_code == 0x6) {
|
||||
munmap(addr, PAGE_SIZE);
|
||||
SKIP(return, "Kernel does not support adding pages to initialized enclave");
|
||||
}
|
||||
|
||||
EXPECT_EQ(self->run.exception_error_code, 0x8007);
|
||||
|
||||
self->run.exception_vector = 0;
|
||||
self->run.exception_error_code = 0;
|
||||
self->run.exception_addr = 0;
|
||||
|
||||
/* Handle AEX by running EACCEPT from new entry point. */
|
||||
self->run.tcs = self->encl.encl_base + PAGE_SIZE;
|
||||
|
||||
eaccept_op.epc_addr = self->encl.encl_base + total_size;
|
||||
eaccept_op.flags = SGX_SECINFO_R | SGX_SECINFO_W | SGX_SECINFO_REG | SGX_SECINFO_PENDING;
|
||||
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);
|
||||
|
||||
/* Can now return to main TCS to resume execution. */
|
||||
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);
|
||||
|
||||
/*
|
||||
* Read memory from newly added page that was just written to,
|
||||
* confirming that data previously written (MAGIC) is present.
|
||||
*/
|
||||
get_addr_op.value = 0;
|
||||
get_addr_op.addr = (unsigned long)addr;
|
||||
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);
|
||||
|
||||
munmap(addr, PAGE_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test for the addition of pages to an initialized enclave via a
|
||||
* pre-emptive run of EACCEPT on page to be added.
|
||||
*/
|
||||
TEST_F(enclave, augment_via_eaccept)
|
||||
{
|
||||
struct encl_op_get_from_addr get_addr_op;
|
||||
struct encl_op_put_to_addr put_addr_op;
|
||||
struct encl_op_eaccept eaccept_op;
|
||||
size_t total_size = 0;
|
||||
void *addr;
|
||||
int i;
|
||||
|
||||
if (!sgx2_supported())
|
||||
SKIP(return, "SGX2 not supported");
|
||||
|
||||
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;
|
||||
|
||||
for (i = 0; i < self->encl.nr_segments; i++) {
|
||||
struct encl_segment *seg = &self->encl.segment_tbl[i];
|
||||
|
||||
total_size += seg->size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Actual enclave size is expected to be larger than the loaded
|
||||
* test enclave since enclave size must be a power of 2 in bytes while
|
||||
* test_encl does not consume it all.
|
||||
*/
|
||||
EXPECT_LT(total_size + PAGE_SIZE, self->encl.encl_size);
|
||||
|
||||
/*
|
||||
* mmap() a page at end of existing enclave to be used for dynamic
|
||||
* EPC page.
|
||||
*
|
||||
* Kernel will allow new mapping using any permissions if it
|
||||
* falls into the enclave's address range but not backed
|
||||
* by existing enclave pages.
|
||||
*/
|
||||
|
||||
addr = mmap((void *)self->encl.encl_base + total_size, PAGE_SIZE,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED,
|
||||
self->encl.fd, 0);
|
||||
EXPECT_NE(addr, MAP_FAILED);
|
||||
|
||||
self->run.exception_vector = 0;
|
||||
self->run.exception_error_code = 0;
|
||||
self->run.exception_addr = 0;
|
||||
|
||||
/*
|
||||
* Run EACCEPT on new page to trigger the #PF->EAUG->EACCEPT(again
|
||||
* without a #PF). All should be transparent to userspace.
|
||||
*/
|
||||
eaccept_op.epc_addr = self->encl.encl_base + total_size;
|
||||
eaccept_op.flags = SGX_SECINFO_R | SGX_SECINFO_W | SGX_SECINFO_REG | SGX_SECINFO_PENDING;
|
||||
eaccept_op.ret = 0;
|
||||
eaccept_op.header.type = ENCL_OP_EACCEPT;
|
||||
|
||||
EXPECT_EQ(ENCL_CALL(&eaccept_op, &self->run, true), 0);
|
||||
|
||||
if (self->run.exception_vector == 14 &&
|
||||
self->run.exception_error_code == 4 &&
|
||||
self->run.exception_addr == self->encl.encl_base + total_size) {
|
||||
munmap(addr, PAGE_SIZE);
|
||||
SKIP(return, "Kernel does not support adding pages to initialized enclave");
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/*
|
||||
* New page should be accessible from within enclave - attempt to
|
||||
* write to it.
|
||||
*/
|
||||
put_addr_op.value = MAGIC;
|
||||
put_addr_op.addr = (unsigned long)addr;
|
||||
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 from newly added page that was just written to,
|
||||
* confirming that data previously written (MAGIC) is present.
|
||||
*/
|
||||
get_addr_op.value = 0;
|
||||
get_addr_op.addr = (unsigned long)addr;
|
||||
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);
|
||||
|
||||
munmap(addr, PAGE_SIZE);
|
||||
}
|
||||
|
||||
TEST_HARNESS_MAIN
|
||||
|
|
Loading…
Reference in New Issue