Skip to content

Commit

Permalink
selftests/sgx: Test two different SGX2 EAUG flows
Browse files Browse the repository at this point in the history
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.

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
  • Loading branch information
rchatre authored and hansendc committed Jul 7, 2022
1 parent 7088c81 commit 67f1f70
Showing 1 changed file with 250 additions and 0 deletions.
250 changes: 250 additions & 0 deletions tools/testing/selftests/sgx/main.c
Expand Up @@ -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;
Expand Down Expand Up @@ -840,4 +849,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

0 comments on commit 67f1f70

Please sign in to comment.