Skip to content

Commit abc5cec

Browse files
rchatrehansendc
authored andcommitted
selftests/sgx: Add page permission and exception test
The Enclave Page Cache Map (EPCM) is a secure structure used by the processor to track the contents of the enclave page cache. The EPCM contains permissions with which enclave pages can be accessed. SGX support allows EPCM and PTE page permissions to differ - as long as the PTE permissions do not exceed the EPCM permissions. Add a test that: (1) Creates an SGX enclave page with writable EPCM permission. (2) Changes the PTE permission on the page to read-only. This should be permitted because the permission does not exceed the EPCM permission. (3) Attempts a write to the page. This should generate a page fault (#PF) because of the read-only PTE even though the EPCM permissions allow the page to be written to. This introduces the first test of SGX exception handling. In this test the issue that caused the exception (PTE page permissions) can be fixed from outside the enclave and after doing so it is possible to re-enter enclave at original entrypoint with ERESUME. Signed-off-by: Reinette Chatre <[email protected]> Signed-off-by: Dave Hansen <[email protected]> Reviewed-by: Jarkko Sakkinen <[email protected]> Acked-by: Dave Hansen <[email protected]> Link: https://lkml.kernel.org/r/3bcc73a4b9fe8780bdb40571805e7ced59e01df7.1636997631.git.reinette.chatre@intel.com
1 parent c085dfc commit abc5cec

File tree

3 files changed

+169
-0
lines changed

3 files changed

+169
-0
lines changed

tools/testing/selftests/sgx/defines.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
enum encl_op_type {
2222
ENCL_OP_PUT_TO_BUFFER,
2323
ENCL_OP_GET_FROM_BUFFER,
24+
ENCL_OP_PUT_TO_ADDRESS,
25+
ENCL_OP_GET_FROM_ADDRESS,
2426
ENCL_OP_MAX,
2527
};
2628

@@ -38,4 +40,16 @@ struct encl_op_get_from_buf {
3840
uint64_t value;
3941
};
4042

43+
struct encl_op_put_to_addr {
44+
struct encl_op_header header;
45+
uint64_t value;
46+
uint64_t addr;
47+
};
48+
49+
struct encl_op_get_from_addr {
50+
struct encl_op_header header;
51+
uint64_t value;
52+
uint64_t addr;
53+
};
54+
4155
#endif /* DEFINES_H */

tools/testing/selftests/sgx/main.c

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "main.h"
2222

2323
static const uint64_t MAGIC = 0x1122334455667788ULL;
24+
static const uint64_t MAGIC2 = 0x8877665544332211ULL;
2425
vdso_sgx_enter_enclave_t vdso_sgx_enter_enclave;
2526

2627
struct vdso_symtab {
@@ -107,6 +108,25 @@ static Elf64_Sym *vdso_symtab_get(struct vdso_symtab *symtab, const char *name)
107108
return NULL;
108109
}
109110

111+
/*
112+
* Return the offset in the enclave where the data segment can be found.
113+
* The first RW segment loaded is the TCS, skip that to get info on the
114+
* data segment.
115+
*/
116+
static off_t encl_get_data_offset(struct encl *encl)
117+
{
118+
int i;
119+
120+
for (i = 1; i < encl->nr_segments; i++) {
121+
struct encl_segment *seg = &encl->segment_tbl[i];
122+
123+
if (seg->prot == (PROT_READ | PROT_WRITE))
124+
return seg->offset;
125+
}
126+
127+
return -1;
128+
}
129+
110130
FIXTURE(enclave) {
111131
struct encl encl;
112132
struct sgx_enclave_run run;
@@ -389,4 +409,118 @@ TEST_F(enclave, clobbered_vdso_and_user_function)
389409
EXPECT_EQ(self->run.user_data, 0);
390410
}
391411

412+
/*
413+
* Second page of .data segment is used to test changing PTE permissions.
414+
* This spans the local encl_buffer within the test enclave.
415+
*
416+
* 1) Start with a sanity check: a value is written to the target page within
417+
* the enclave and read back to ensure target page can be written to.
418+
* 2) Change PTE permissions (RW -> RO) of target page within enclave.
419+
* 3) Repeat (1) - this time expecting a regular #PF communicated via the
420+
* vDSO.
421+
* 4) Change PTE permissions of target page within enclave back to be RW.
422+
* 5) Repeat (1) by resuming enclave, now expected to be possible to write to
423+
* and read from target page within enclave.
424+
*/
425+
TEST_F(enclave, pte_permissions)
426+
{
427+
struct encl_op_get_from_addr get_addr_op;
428+
struct encl_op_put_to_addr put_addr_op;
429+
unsigned long data_start;
430+
int ret;
431+
432+
ASSERT_TRUE(setup_test_encl(ENCL_HEAP_SIZE_DEFAULT, &self->encl, _metadata));
433+
434+
memset(&self->run, 0, sizeof(self->run));
435+
self->run.tcs = self->encl.encl_base;
436+
437+
data_start = self->encl.encl_base +
438+
encl_get_data_offset(&self->encl) +
439+
PAGE_SIZE;
440+
441+
/*
442+
* Sanity check to ensure it is possible to write to page that will
443+
* have its permissions manipulated.
444+
*/
445+
446+
/* Write MAGIC to page */
447+
put_addr_op.value = MAGIC;
448+
put_addr_op.addr = data_start;
449+
put_addr_op.header.type = ENCL_OP_PUT_TO_ADDRESS;
450+
451+
EXPECT_EQ(ENCL_CALL(&put_addr_op, &self->run, true), 0);
452+
453+
EXPECT_EEXIT(&self->run);
454+
EXPECT_EQ(self->run.exception_vector, 0);
455+
EXPECT_EQ(self->run.exception_error_code, 0);
456+
EXPECT_EQ(self->run.exception_addr, 0);
457+
458+
/*
459+
* Read memory that was just written to, confirming that it is the
460+
* value previously written (MAGIC).
461+
*/
462+
get_addr_op.value = 0;
463+
get_addr_op.addr = data_start;
464+
get_addr_op.header.type = ENCL_OP_GET_FROM_ADDRESS;
465+
466+
EXPECT_EQ(ENCL_CALL(&get_addr_op, &self->run, true), 0);
467+
468+
EXPECT_EQ(get_addr_op.value, MAGIC);
469+
EXPECT_EEXIT(&self->run);
470+
EXPECT_EQ(self->run.exception_vector, 0);
471+
EXPECT_EQ(self->run.exception_error_code, 0);
472+
EXPECT_EQ(self->run.exception_addr, 0);
473+
474+
/* Change PTE permissions of target page within the enclave */
475+
ret = mprotect((void *)data_start, PAGE_SIZE, PROT_READ);
476+
if (ret)
477+
perror("mprotect");
478+
479+
/*
480+
* PTE permissions of target page changed to read-only, EPCM
481+
* permissions unchanged (EPCM permissions are RW), attempt to
482+
* write to the page, expecting a regular #PF.
483+
*/
484+
485+
put_addr_op.value = MAGIC2;
486+
487+
EXPECT_EQ(ENCL_CALL(&put_addr_op, &self->run, true), 0);
488+
489+
EXPECT_EQ(self->run.exception_vector, 14);
490+
EXPECT_EQ(self->run.exception_error_code, 0x7);
491+
EXPECT_EQ(self->run.exception_addr, data_start);
492+
493+
self->run.exception_vector = 0;
494+
self->run.exception_error_code = 0;
495+
self->run.exception_addr = 0;
496+
497+
/*
498+
* Change PTE permissions back to enable enclave to write to the
499+
* target page and resume enclave - do not expect any exceptions this
500+
* time.
501+
*/
502+
ret = mprotect((void *)data_start, PAGE_SIZE, PROT_READ | PROT_WRITE);
503+
if (ret)
504+
perror("mprotect");
505+
506+
EXPECT_EQ(vdso_sgx_enter_enclave((unsigned long)&put_addr_op, 0,
507+
0, ERESUME, 0, 0, &self->run),
508+
0);
509+
510+
EXPECT_EEXIT(&self->run);
511+
EXPECT_EQ(self->run.exception_vector, 0);
512+
EXPECT_EQ(self->run.exception_error_code, 0);
513+
EXPECT_EQ(self->run.exception_addr, 0);
514+
515+
get_addr_op.value = 0;
516+
517+
EXPECT_EQ(ENCL_CALL(&get_addr_op, &self->run, true), 0);
518+
519+
EXPECT_EQ(get_addr_op.value, MAGIC2);
520+
EXPECT_EEXIT(&self->run);
521+
EXPECT_EQ(self->run.exception_vector, 0);
522+
EXPECT_EQ(self->run.exception_error_code, 0);
523+
EXPECT_EQ(self->run.exception_addr, 0);
524+
}
525+
392526
TEST_HARNESS_MAIN

tools/testing/selftests/sgx/test_encl.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
#include <stddef.h>
55
#include "defines.h"
66

7+
/*
8+
* Data buffer spanning two pages that will be placed first in .data
9+
* segment. Even if not used internally the second page is needed by
10+
* external test manipulating page permissions.
11+
*/
712
static uint8_t encl_buffer[8192] = { 1 };
813

914
static void *memcpy(void *dest, const void *src, size_t n)
@@ -30,11 +35,27 @@ static void do_encl_op_get_from_buf(void *op)
3035
memcpy(&op2->value, &encl_buffer[0], 8);
3136
}
3237

38+
static void do_encl_op_put_to_addr(void *_op)
39+
{
40+
struct encl_op_put_to_addr *op = _op;
41+
42+
memcpy((void *)op->addr, &op->value, 8);
43+
}
44+
45+
static void do_encl_op_get_from_addr(void *_op)
46+
{
47+
struct encl_op_get_from_addr *op = _op;
48+
49+
memcpy(&op->value, (void *)op->addr, 8);
50+
}
51+
3352
void encl_body(void *rdi, void *rsi)
3453
{
3554
const void (*encl_op_array[ENCL_OP_MAX])(void *) = {
3655
do_encl_op_put_to_buf,
3756
do_encl_op_get_from_buf,
57+
do_encl_op_put_to_addr,
58+
do_encl_op_get_from_addr,
3859
};
3960

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

0 commit comments

Comments
 (0)