for (i = buckettab[elf_sym_hash(name) % bucketnum]; i != STN_UNDEF;
i = chaintab[i]) {
sym = &symtab->elf_symtab[i]; if (!strcmp(name, &symtab->elf_symstrtab[sym->st_name])) return sym;
}
return NULL;
}
/* * Return the offset in the enclave where the TCS segment can be found. * The first RW segment loaded is the TCS.
*/ static off_t encl_get_tcs_offset(struct encl *encl)
{ int i;
for (i = 0; i < encl->nr_segments; i++) { struct encl_segment *seg = &encl->segment_tbl[i];
if (i == 0 && seg->prot == (PROT_READ | PROT_WRITE)) return seg->offset;
}
return -1;
}
/* * Return the offset in the enclave where the data segment can be found. * The first RW segment loaded is the TCS, skip that to get info on the * data segment.
*/ static off_t encl_get_data_offset(struct encl *encl)
{ int i;
for (i = 1; i < encl->nr_segments; i++) { struct encl_segment *seg = &encl->segment_tbl[i];
if (seg->prot == (PROT_READ | PROT_WRITE)) return seg->offset;
}
/* * A section metric is concatenated in a way that @low bits 12-31 define the * bits 12-31 of the metric and @high bits 0-19 define the bits 32-51 of the * metric.
*/ staticunsignedlong sgx_calc_section_metric(unsignedint low, unsignedint high)
{ return (low & GENMASK_ULL(31, 12)) +
((high & GENMASK_ULL(19, 0)) << 32);
}
/* * Sum total available physical SGX memory across all EPC sections * * Return: total available physical SGX memory available on system
*/ staticunsignedlong get_total_epc_mem(void)
{ unsignedint eax, ebx, ecx, edx; unsignedlong total_size = 0; unsignedint type; int section = 0;
/* * Create enclave with additional heap that is as big as all * available physical SGX memory.
*/
total_mem = get_total_epc_mem();
ASSERT_NE(total_mem, 0);
TH_LOG("Creating an enclave with %lu bytes heap may take a while ...",
total_mem);
ASSERT_TRUE(setup_test_encl(total_mem, &self->encl, _metadata));
/* * Hardware (SGX2) and kernel support is needed for this test. Start * with check that test has a chance of succeeding.
*/
memset(&modt_ioc, 0, sizeof(modt_ioc));
ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_MODIFY_TYPES, &modt_ioc);
if (ret == -1) { if (errno == ENOTTY)
SKIP(return, "Kernel does not support SGX_IOC_ENCLAVE_MODIFY_TYPES ioctl()"); elseif (errno == ENODEV)
SKIP(return, "System does not support SGX2");
}
/* * Invalid parameters were provided during sanity check, * expect command to fail.
*/
EXPECT_EQ(ret, -1);
/* SGX2 is supported by kernel and hardware, test can proceed. */
memset(&self->run, 0, sizeof(self->run));
self->run.tcs = self->encl.encl_base;
TH_LOG("Changing type of %zd bytes to trimmed may take a while ...",
heap->size);
ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_MODIFY_TYPES, &modt_ioc);
errno_save = ret == -1 ? errno : 0;
TH_LOG("Entering enclave to run EACCEPT for each page of %zd bytes may take a while ...",
heap->size); for (i = 0; i < heap->size; i += 4096) {
eaccept_op.epc_addr = addr + i;
eaccept_op.ret = 0;
TH_LOG("Removing %zd bytes from enclave may take a while ...",
heap->size);
ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_REMOVE_PAGES, &remove_ioc);
errno_save = ret == -1 ? errno : 0;
/* * Second page of .data segment is used to test changing PTE permissions. * This spans the local encl_buffer within the test enclave. * * 1) Start with a sanity check: a value is written to the target page within * the enclave and read back to ensure target page can be written to. * 2) Change PTE permissions (RW -> RO) of target page within enclave. * 3) Repeat (1) - this time expecting a regular #PF communicated via the * vDSO. * 4) Change PTE permissions of target page within enclave back to be RW. * 5) Repeat (1) by resuming enclave, now expected to be possible to write to * and read from target page within enclave.
*/
TEST_F(enclave, pte_permissions)
{ struct encl_op_get_from_addr get_addr_op; struct encl_op_put_to_addr put_addr_op; unsignedlong data_start; int ret;
/* * Read memory that was just written to, confirming that it is the * value previously written (MAGIC).
*/
get_addr_op.value = 0;
get_addr_op.addr = data_start;
get_addr_op.header.type = ENCL_OP_GET_FROM_ADDRESS;
/* Change PTE permissions of target page within the enclave */
ret = mprotect((void *)data_start, PAGE_SIZE, PROT_READ); if (ret)
perror("mprotect");
/* * PTE permissions of target page changed to read-only, EPCM * permissions unchanged (EPCM permissions are RW), attempt to * write to the page, expecting a regular #PF.
*/
/* * Change PTE permissions back to enable enclave to write to the * target page and resume enclave - do not expect any exceptions this * time.
*/
ret = mprotect((void *)data_start, PAGE_SIZE, PROT_READ | PROT_WRITE); if (ret)
perror("mprotect");
/* * Modifying permissions of TCS page should not be possible.
*/
TEST_F(enclave, tcs_permissions)
{ struct sgx_enclave_restrict_permissions ioc; int ret, errno_save;
/* * Ensure kernel supports needed ioctl() and system supports needed * commands.
*/
ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS, &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()"); elseif (errno_save == ENODEV)
SKIP(return, "System does not support SGX2");
/* * Attempt to make TCS page read-only. This is not allowed and * should be prevented by the kernel.
*/
ioc.offset = encl_get_tcs_offset(&self->encl);
ioc.length = PAGE_SIZE;
ioc.permissions = SGX_SECINFO_R;
ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS, &ioc);
errno_save = ret == -1 ? errno : 0;
/* * Ensure kernel supports needed ioctl() and system supports needed * commands.
*/
memset(&restrict_ioc, 0, sizeof(restrict_ioc));
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()"); elseif (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 and * VMA permissions of RW.
*/
/* * 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;
/* * 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;
/* * EPCM permissions of page is now read-only, expect #PF * on EPCM when attempting to write to page from within enclave.
*/
put_addr_op.value = MAGIC2;
/* * 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.
*/
/* * Attempt to return to main TCS to resume execution at faulting * instruction, PTE should continue to allow writing to the page.
*/
self->run.tcs = self->encl.encl_base;
/* * Wrong page permissions that caused original fault has * now been fixed via EPCM permissions. * Resume execution in main TCS to re-attempt the memory access.
*/
self->run.tcs = self->encl.encl_base;
/* * 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");
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);
/* * 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 = (unsignedlong)addr;
put_addr_op.header.type = ENCL_OP_PUT_TO_ADDRESS;
/* * 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 = (unsignedlong)addr;
get_addr_op.header.type = ENCL_OP_GET_FROM_ADDRESS;
/* * 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");
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.
*/
/* * 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;
/* * New page should be accessible from within enclave - attempt to * write to it.
*/
put_addr_op.value = MAGIC;
put_addr_op.addr = (unsignedlong)addr;
put_addr_op.header.type = ENCL_OP_PUT_TO_ADDRESS;
/* * 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 = (unsignedlong)addr;
get_addr_op.header.type = ENCL_OP_GET_FROM_ADDRESS;
/* * SGX2 page type modification test in two phases: * Phase 1: * Create a new TCS, consisting out of three new pages (stack page with regular * page type, SSA page with regular page type, and TCS page with TCS page * type) in an initialized enclave and run a simple workload within it. * Phase 2: * Remove the three pages added in phase 1, add a new regular page at the * same address that previously hosted the TCS page and verify that it can * be modified.
*/
TEST_F(enclave, tcs_create)
{ struct encl_op_init_tcs_page init_tcs_page_op; struct sgx_enclave_remove_pages remove_ioc; struct encl_op_get_from_addr get_addr_op; struct sgx_enclave_modify_types modt_ioc; struct encl_op_put_to_addr put_addr_op; struct encl_op_get_from_buf get_buf_op; struct encl_op_put_to_buf put_buf_op; void *addr, *tcs, *stack_end, *ssa; struct encl_op_eaccept eaccept_op;
size_t total_size = 0;
uint64_t val_64; int errno_save; int ret, i;
/* * Hardware (SGX2) and kernel support is needed for this test. Start * with check that test has a chance of succeeding.
*/
memset(&modt_ioc, 0, sizeof(modt_ioc));
ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_MODIFY_TYPES, &modt_ioc);
if (ret == -1) { if (errno == ENOTTY)
SKIP(return, "Kernel does not support SGX_IOC_ENCLAVE_MODIFY_TYPES ioctl()"); elseif (errno == ENODEV)
SKIP(return, "System does not support SGX2");
}
/* * Invalid parameters were provided during sanity check, * expect command to fail.
*/
EXPECT_EQ(ret, -1);
/* * Add three regular pages via EAUG: one will be the TCS stack, one * will be the TCS SSA, and one will be the new TCS. The stack and * SSA will remain as regular pages, the TCS page will need its * type changed after populated with needed data.
*/ 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 + 3 * PAGE_SIZE, self->encl.encl_size);
/* * mmap() three pages at end of existing enclave to be used for the * three new pages.
*/
addr = mmap((void *)self->encl.encl_base + total_size, 3 * PAGE_SIZE,
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED,
self->encl.fd, 0);
EXPECT_NE(addr, MAP_FAILED);
/* * Three new pages added to enclave. Now populate the TCS page with * needed data. This should be done from within enclave. Provide * the function that will do the actual data population with needed * data.
*/
/* * New TCS will use the "encl_dyn_entry" entrypoint that expects * stack to begin in page before TCS page.
*/
val_64 = encl_get_entry(&self->encl, "encl_dyn_entry");
EXPECT_NE(val_64, 0);
/* * Phase 2 of test: * Remove pages associated with new TCS, create a regular page * where TCS page used to be and verify it can be used as a regular * page.
*/
/* Start page removal by requesting change of page type to PT_TRIM. */
memset(&modt_ioc, 0, sizeof(modt_ioc));
/* * Enter enclave via TCS #1 and access location where TCS #3 was to * trigger dynamic add of regular page at that location.
*/
eaccept_op.epc_addr = (unsignedlong)tcs;
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;
/* * New page should be accessible from within enclave - write to it.
*/
put_addr_op.value = MAGIC;
put_addr_op.addr = (unsignedlong)tcs;
put_addr_op.header.type = ENCL_OP_PUT_TO_ADDRESS;
/* * 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 = (unsignedlong)tcs;
get_addr_op.header.type = ENCL_OP_GET_FROM_ADDRESS;
/* * Ensure sane behavior if user requests page removal, does not run * EACCEPT from within enclave but still attempts to finalize page removal * with the SGX_IOC_ENCLAVE_REMOVE_PAGES ioctl(). The latter should fail * because the removal was not EACCEPTed from within the enclave.
*/
TEST_F(enclave, remove_added_page_no_eaccept)
{ struct sgx_enclave_remove_pages remove_ioc; struct encl_op_get_from_addr get_addr_op; struct sgx_enclave_modify_types modt_ioc; struct encl_op_put_to_addr put_addr_op; unsignedlong data_start; int ret, errno_save;
/* * Hardware (SGX2) and kernel support is needed for this test. Start * with check that test has a chance of succeeding.
*/
memset(&modt_ioc, 0, sizeof(modt_ioc));
ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_MODIFY_TYPES, &modt_ioc);
if (ret == -1) { if (errno == ENOTTY)
SKIP(return, "Kernel does not support SGX_IOC_ENCLAVE_MODIFY_TYPES ioctl()"); elseif (errno == ENODEV)
SKIP(return, "System does not support SGX2");
}
/* * Invalid parameters were provided during sanity check, * expect command to fail.
*/
EXPECT_EQ(ret, -1);
/* * Page that will be removed is the second data page in the .data * segment. This forms part of the local encl_buffer within the * enclave.
*/
data_start = self->encl.encl_base +
encl_get_data_offset(&self->encl) + PAGE_SIZE;
/* * Sanity check that page at @data_start is writable before * removing it. * * 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;
/* * Read memory that was just written to, confirming that data * previously written (MAGIC) is present.
*/
get_addr_op.value = 0;
get_addr_op.addr = data_start;
get_addr_op.header.type = ENCL_OP_GET_FROM_ADDRESS;
ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_REMOVE_PAGES, &remove_ioc);
errno_save = ret == -1 ? errno : 0;
/* Operation not permitted since EACCEPT was omitted. */
EXPECT_EQ(ret, -1);
EXPECT_EQ(errno_save, EPERM);
EXPECT_EQ(remove_ioc.count, 0);
}
/* * Request enclave page removal but instead of correctly following with * EACCEPT a read attempt to page is made from within the enclave.
*/
TEST_F(enclave, remove_added_page_invalid_access)
{ struct encl_op_get_from_addr get_addr_op; struct encl_op_put_to_addr put_addr_op; struct sgx_enclave_modify_types ioc; unsignedlong data_start; int ret, errno_save;
/* * Hardware (SGX2) and kernel support is needed for this test. Start * with check that test has a chance of succeeding.
*/
memset(&ioc, 0, sizeof(ioc));
ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_MODIFY_TYPES, &ioc);
if (ret == -1) { if (errno == ENOTTY)
SKIP(return, "Kernel does not support SGX_IOC_ENCLAVE_MODIFY_TYPES ioctl()"); elseif (errno == ENODEV)
SKIP(return, "System does not support SGX2");
}
/* * Invalid parameters were provided during sanity check, * expect command to fail.
*/
EXPECT_EQ(ret, -1);
/* * Page that will be removed is the second data page in the .data * segment. This forms part of the local encl_buffer within the * enclave.
*/
data_start = self->encl.encl_base +
encl_get_data_offset(&self->encl) + PAGE_SIZE;
/* * Sanity check that page at @data_start is writable before * removing it. * * 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;
/* * Read memory that was just written to, confirming that data * previously written (MAGIC) is present.
*/
get_addr_op.value = 0;
get_addr_op.addr = data_start;
get_addr_op.header.type = ENCL_OP_GET_FROM_ADDRESS;
/* * Request enclave page removal and correctly follow with * EACCEPT but do not follow with removal ioctl() but instead a read attempt * to removed page is made from within the enclave.
*/
TEST_F(enclave, remove_added_page_invalid_access_after_eaccept)
{ struct encl_op_get_from_addr get_addr_op; struct encl_op_put_to_addr put_addr_op; struct sgx_enclave_modify_types ioc; struct encl_op_eaccept eaccept_op; unsignedlong data_start; int ret, errno_save;
/* * Hardware (SGX2) and kernel support is needed for this test. Start * with check that test has a chance of succeeding.
*/
memset(&ioc, 0, sizeof(ioc));
ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_MODIFY_TYPES, &ioc);
if (ret == -1) { if (errno == ENOTTY)
SKIP(return, "Kernel does not support SGX_IOC_ENCLAVE_MODIFY_TYPES ioctl()"); elseif (errno == ENODEV)
SKIP(return, "System does not support SGX2");
}
/* * Invalid parameters were provided during sanity check, * expect command to fail.
*/
EXPECT_EQ(ret, -1);
/* * Page that will be removed is the second data page in the .data * segment. This forms part of the local encl_buffer within the * enclave.
*/
data_start = self->encl.encl_base +
encl_get_data_offset(&self->encl) + PAGE_SIZE;
/* * Sanity check that page at @data_start is writable before * removing it. * * 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;
/* * Read memory that was just written to, confirming that data * previously written (MAGIC) is present.
*/
get_addr_op.value = 0;
get_addr_op.addr = data_start;
get_addr_op.header.type = ENCL_OP_GET_FROM_ADDRESS;
/* * Hardware (SGX2) and kernel support is needed for this test. Start * with check that test has a chance of succeeding.
*/
memset(&modt_ioc, 0, sizeof(modt_ioc));
ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_MODIFY_TYPES, &modt_ioc);
if (ret == -1) { if (errno == ENOTTY)
SKIP(return, "Kernel does not support SGX_IOC_ENCLAVE_MODIFY_TYPES ioctl()"); elseif (errno == ENODEV)
SKIP(return, "System does not support SGX2");
}
/* * Invalid parameters were provided during sanity check, * expect command to fail.
*/
EXPECT_EQ(ret, -1);
/* SGX2 is supported by kernel and hardware, test can proceed. */
memset(&self->run, 0, sizeof(self->run));
self->run.tcs = self->encl.encl_base;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.