Skip to content

Commit 20404a8

Browse files
rchatrehansendc
authored andcommitted
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. 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 needed 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. 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 EPCM permissions since the page table entry continues to allow RW access. 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 succeed since both EPCM and PTE permissions allow this access. Signed-off-by: Reinette Chatre <[email protected]> Signed-off-by: Dave Hansen <[email protected]> Acked-by: Jarkko Sakkinen <[email protected]> Link: https://lkml.kernel.org/r/2617bf2b2d1e27ca1d0096e1192ae5896baf3f80.1652137848.git.reinette.chatre@intel.com
1 parent 629b515 commit 20404a8

File tree

3 files changed

+267
-0
lines changed

3 files changed

+267
-0
lines changed

tools/testing/selftests/sgx/defines.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ enum encl_op_type {
2424
ENCL_OP_PUT_TO_ADDRESS,
2525
ENCL_OP_GET_FROM_ADDRESS,
2626
ENCL_OP_NOP,
27+
ENCL_OP_EACCEPT,
28+
ENCL_OP_EMODPE,
2729
ENCL_OP_MAX,
2830
};
2931

@@ -53,4 +55,17 @@ struct encl_op_get_from_addr {
5355
uint64_t addr;
5456
};
5557

58+
struct encl_op_eaccept {
59+
struct encl_op_header header;
60+
uint64_t epc_addr;
61+
uint64_t flags;
62+
uint64_t ret;
63+
};
64+
65+
struct encl_op_emodpe {
66+
struct encl_op_header header;
67+
uint64_t epc_addr;
68+
uint64_t flags;
69+
};
70+
5671
#endif /* DEFINES_H */

tools/testing/selftests/sgx/main.c

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@ static const uint64_t MAGIC = 0x1122334455667788ULL;
2525
static const uint64_t MAGIC2 = 0x8877665544332211ULL;
2626
vdso_sgx_enter_enclave_t vdso_sgx_enter_enclave;
2727

28+
/*
29+
* Security Information (SECINFO) data structure needed by a few SGX
30+
* instructions (eg. ENCLU[EACCEPT] and ENCLU[EMODPE]) holds meta-data
31+
* about an enclave page. &enum sgx_secinfo_page_state specifies the
32+
* secinfo flags used for page state.
33+
*/
34+
enum sgx_secinfo_page_state {
35+
SGX_SECINFO_PENDING = (1 << 3),
36+
SGX_SECINFO_MODIFIED = (1 << 4),
37+
SGX_SECINFO_PR = (1 << 5),
38+
};
39+
2840
struct vdso_symtab {
2941
Elf64_Sym *elf_symtab;
3042
const char *elf_symstrtab;
@@ -555,4 +567,206 @@ TEST_F(enclave, pte_permissions)
555567
EXPECT_EQ(self->run.exception_addr, 0);
556568
}
557569

570+
/*
571+
* Enclave page permission test.
572+
*
573+
* Modify and restore enclave page's EPCM (enclave) permissions from
574+
* outside enclave (ENCLS[EMODPR] via kernel) as well as from within
575+
* enclave (via ENCLU[EMODPE]). Check for page fault if
576+
* VMA allows access but EPCM permissions do not.
577+
*/
578+
TEST_F(enclave, epcm_permissions)
579+
{
580+
struct sgx_enclave_restrict_permissions restrict_ioc;
581+
struct encl_op_get_from_addr get_addr_op;
582+
struct encl_op_put_to_addr put_addr_op;
583+
struct encl_op_eaccept eaccept_op;
584+
struct encl_op_emodpe emodpe_op;
585+
unsigned long data_start;
586+
int ret, errno_save;
587+
588+
ASSERT_TRUE(setup_test_encl(ENCL_HEAP_SIZE_DEFAULT, &self->encl, _metadata));
589+
590+
memset(&self->run, 0, sizeof(self->run));
591+
self->run.tcs = self->encl.encl_base;
592+
593+
/*
594+
* Ensure kernel supports needed ioctl() and system supports needed
595+
* commands.
596+
*/
597+
memset(&restrict_ioc, 0, sizeof(restrict_ioc));
598+
599+
ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS,
600+
&restrict_ioc);
601+
errno_save = ret == -1 ? errno : 0;
602+
603+
/*
604+
* Invalid parameters were provided during sanity check,
605+
* expect command to fail.
606+
*/
607+
ASSERT_EQ(ret, -1);
608+
609+
/* ret == -1 */
610+
if (errno_save == ENOTTY)
611+
SKIP(return,
612+
"Kernel does not support SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS ioctl()");
613+
else if (errno_save == ENODEV)
614+
SKIP(return, "System does not support SGX2");
615+
616+
/*
617+
* Page that will have its permissions changed is the second data
618+
* page in the .data segment. This forms part of the local encl_buffer
619+
* within the enclave.
620+
*
621+
* At start of test @data_start should have EPCM as well as PTE and
622+
* VMA permissions of RW.
623+
*/
624+
625+
data_start = self->encl.encl_base +
626+
encl_get_data_offset(&self->encl) + PAGE_SIZE;
627+
628+
/*
629+
* Sanity check that page at @data_start is writable before making
630+
* any changes to page permissions.
631+
*
632+
* Start by writing MAGIC to test page.
633+
*/
634+
put_addr_op.value = MAGIC;
635+
put_addr_op.addr = data_start;
636+
put_addr_op.header.type = ENCL_OP_PUT_TO_ADDRESS;
637+
638+
EXPECT_EQ(ENCL_CALL(&put_addr_op, &self->run, true), 0);
639+
640+
EXPECT_EEXIT(&self->run);
641+
EXPECT_EQ(self->run.exception_vector, 0);
642+
EXPECT_EQ(self->run.exception_error_code, 0);
643+
EXPECT_EQ(self->run.exception_addr, 0);
644+
645+
/*
646+
* Read memory that was just written to, confirming that
647+
* page is writable.
648+
*/
649+
get_addr_op.value = 0;
650+
get_addr_op.addr = data_start;
651+
get_addr_op.header.type = ENCL_OP_GET_FROM_ADDRESS;
652+
653+
EXPECT_EQ(ENCL_CALL(&get_addr_op, &self->run, true), 0);
654+
655+
EXPECT_EQ(get_addr_op.value, MAGIC);
656+
EXPECT_EEXIT(&self->run);
657+
EXPECT_EQ(self->run.exception_vector, 0);
658+
EXPECT_EQ(self->run.exception_error_code, 0);
659+
EXPECT_EQ(self->run.exception_addr, 0);
660+
661+
/*
662+
* Change EPCM permissions to read-only. Kernel still considers
663+
* the page writable.
664+
*/
665+
memset(&restrict_ioc, 0, sizeof(restrict_ioc));
666+
667+
restrict_ioc.offset = encl_get_data_offset(&self->encl) + PAGE_SIZE;
668+
restrict_ioc.length = PAGE_SIZE;
669+
restrict_ioc.permissions = SGX_SECINFO_R;
670+
671+
ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS,
672+
&restrict_ioc);
673+
errno_save = ret == -1 ? errno : 0;
674+
675+
EXPECT_EQ(ret, 0);
676+
EXPECT_EQ(errno_save, 0);
677+
EXPECT_EQ(restrict_ioc.result, 0);
678+
EXPECT_EQ(restrict_ioc.count, 4096);
679+
680+
/*
681+
* EPCM permissions changed from kernel, need to EACCEPT from enclave.
682+
*/
683+
eaccept_op.epc_addr = data_start;
684+
eaccept_op.flags = SGX_SECINFO_R | SGX_SECINFO_REG | SGX_SECINFO_PR;
685+
eaccept_op.ret = 0;
686+
eaccept_op.header.type = ENCL_OP_EACCEPT;
687+
688+
EXPECT_EQ(ENCL_CALL(&eaccept_op, &self->run, true), 0);
689+
690+
EXPECT_EEXIT(&self->run);
691+
EXPECT_EQ(self->run.exception_vector, 0);
692+
EXPECT_EQ(self->run.exception_error_code, 0);
693+
EXPECT_EQ(self->run.exception_addr, 0);
694+
EXPECT_EQ(eaccept_op.ret, 0);
695+
696+
/*
697+
* EPCM permissions of page is now read-only, expect #PF
698+
* on EPCM when attempting to write to page from within enclave.
699+
*/
700+
put_addr_op.value = MAGIC2;
701+
702+
EXPECT_EQ(ENCL_CALL(&put_addr_op, &self->run, true), 0);
703+
704+
EXPECT_EQ(self->run.function, ERESUME);
705+
EXPECT_EQ(self->run.exception_vector, 14);
706+
EXPECT_EQ(self->run.exception_error_code, 0x8007);
707+
EXPECT_EQ(self->run.exception_addr, data_start);
708+
709+
self->run.exception_vector = 0;
710+
self->run.exception_error_code = 0;
711+
self->run.exception_addr = 0;
712+
713+
/*
714+
* Received AEX but cannot return to enclave at same entrypoint,
715+
* need different TCS from where EPCM permission can be made writable
716+
* again.
717+
*/
718+
self->run.tcs = self->encl.encl_base + PAGE_SIZE;
719+
720+
/*
721+
* Enter enclave at new TCS to change EPCM permissions to be
722+
* writable again and thus fix the page fault that triggered the
723+
* AEX.
724+
*/
725+
726+
emodpe_op.epc_addr = data_start;
727+
emodpe_op.flags = SGX_SECINFO_R | SGX_SECINFO_W;
728+
emodpe_op.header.type = ENCL_OP_EMODPE;
729+
730+
EXPECT_EQ(ENCL_CALL(&emodpe_op, &self->run, true), 0);
731+
732+
EXPECT_EEXIT(&self->run);
733+
EXPECT_EQ(self->run.exception_vector, 0);
734+
EXPECT_EQ(self->run.exception_error_code, 0);
735+
EXPECT_EQ(self->run.exception_addr, 0);
736+
737+
/*
738+
* Attempt to return to main TCS to resume execution at faulting
739+
* instruction, PTE should continue to allow writing to the page.
740+
*/
741+
self->run.tcs = self->encl.encl_base;
742+
743+
/*
744+
* Wrong page permissions that caused original fault has
745+
* now been fixed via EPCM permissions.
746+
* Resume execution in main TCS to re-attempt the memory access.
747+
*/
748+
self->run.tcs = self->encl.encl_base;
749+
750+
EXPECT_EQ(vdso_sgx_enter_enclave((unsigned long)&put_addr_op, 0, 0,
751+
ERESUME, 0, 0,
752+
&self->run),
753+
0);
754+
755+
EXPECT_EEXIT(&self->run);
756+
EXPECT_EQ(self->run.exception_vector, 0);
757+
EXPECT_EQ(self->run.exception_error_code, 0);
758+
EXPECT_EQ(self->run.exception_addr, 0);
759+
760+
get_addr_op.value = 0;
761+
762+
EXPECT_EQ(ENCL_CALL(&get_addr_op, &self->run, true), 0);
763+
764+
EXPECT_EQ(get_addr_op.value, MAGIC2);
765+
EXPECT_EEXIT(&self->run);
766+
EXPECT_EQ(self->run.user_data, 0);
767+
EXPECT_EQ(self->run.exception_vector, 0);
768+
EXPECT_EQ(self->run.exception_error_code, 0);
769+
EXPECT_EQ(self->run.exception_addr, 0);
770+
}
771+
558772
TEST_HARNESS_MAIN

tools/testing/selftests/sgx/test_encl.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,42 @@
1111
*/
1212
static uint8_t encl_buffer[8192] = { 1 };
1313

14+
enum sgx_enclu_function {
15+
EACCEPT = 0x5,
16+
EMODPE = 0x6,
17+
};
18+
19+
static void do_encl_emodpe(void *_op)
20+
{
21+
struct sgx_secinfo secinfo __aligned(sizeof(struct sgx_secinfo)) = {0};
22+
struct encl_op_emodpe *op = _op;
23+
24+
secinfo.flags = op->flags;
25+
26+
asm volatile(".byte 0x0f, 0x01, 0xd7"
27+
:
28+
: "a" (EMODPE),
29+
"b" (&secinfo),
30+
"c" (op->epc_addr));
31+
}
32+
33+
static void do_encl_eaccept(void *_op)
34+
{
35+
struct sgx_secinfo secinfo __aligned(sizeof(struct sgx_secinfo)) = {0};
36+
struct encl_op_eaccept *op = _op;
37+
int rax;
38+
39+
secinfo.flags = op->flags;
40+
41+
asm volatile(".byte 0x0f, 0x01, 0xd7"
42+
: "=a" (rax)
43+
: "a" (EACCEPT),
44+
"b" (&secinfo),
45+
"c" (op->epc_addr));
46+
47+
op->ret = rax;
48+
}
49+
1450
static void *memcpy(void *dest, const void *src, size_t n)
1551
{
1652
size_t i;
@@ -62,6 +98,8 @@ void encl_body(void *rdi, void *rsi)
6298
do_encl_op_put_to_addr,
6399
do_encl_op_get_from_addr,
64100
do_encl_op_nop,
101+
do_encl_eaccept,
102+
do_encl_emodpe,
65103
};
66104

67105
struct encl_op_header *op = (struct encl_op_header *)rdi;

0 commit comments

Comments
 (0)