Message ID | 20190424062623.4345-4-cedric.xing@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | An alternative __vdso_sgx_enter_enclave() to allow enclave/host parameter passing using untrusted stack | expand |
On Tue, Apr 23, 2019 at 11:26:23PM -0700, Cedric Xing wrote: > This patch augments SGX selftest with two new tests. > > The first test exercises the newly added callback interface, by marking the > whole enclave range as PROT_READ, then calling mprotect() upon #PFs to add > necessary PTE permissions per PFEC (#PF Error Code) until the enclave finishes. > This test also serves as an example to demonstrate the callback interface. > > The second test single-steps through __vdso_sgx_enter_enclave() to make sure > the call stack can be unwound at every instruction within that vDSO API. Its > purpose is to validate the hand-crafted CFI directives in the assembly. > > Signed-off-by: Cedric Xing <cedric.xing@intel.com> > --- > tools/testing/selftests/x86/sgx/Makefile | 6 +- > tools/testing/selftests/x86/sgx/main.c | 323 ++++++++++++++++++--- > tools/testing/selftests/x86/sgx/sgx_call.S | 40 ++- > 3 files changed, 322 insertions(+), 47 deletions(-) > > diff --git a/tools/testing/selftests/x86/sgx/Makefile b/tools/testing/selftests/x86/sgx/Makefile > index 3af15d7c8644..31f937e220c4 100644 > --- a/tools/testing/selftests/x86/sgx/Makefile > +++ b/tools/testing/selftests/x86/sgx/Makefile > @@ -14,16 +14,16 @@ TEST_CUSTOM_PROGS := $(OUTPUT)/test_sgx > all_64: $(TEST_CUSTOM_PROGS) > > $(TEST_CUSTOM_PROGS): main.c sgx_call.S $(OUTPUT)/encl_piggy.o > - $(CC) $(HOST_CFLAGS) -o $@ $^ > + $(CC) $(HOST_CFLAGS) -o $@ $^ -lunwind -ldl -Wl,--defsym,__image_base=0 -pie > > $(OUTPUT)/encl_piggy.o: encl_piggy.S $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss > $(CC) $(HOST_CFLAGS) -I$(OUTPUT) -c $< -o $@ > > $(OUTPUT)/encl.bin: $(OUTPUT)/encl.elf > - objcopy --remove-section=.got.plt -O binary $< $@ > + objcopy -O binary $< $@ > > $(OUTPUT)/encl.elf: encl.lds encl.c encl_bootstrap.S > - $(CC) $(ENCL_CFLAGS) -T $^ -o $@ > + $(CC) $(ENCL_CFLAGS) -T $^ -o $@ -Wl,--build-id=none > > $(OUTPUT)/encl.ss: $(OUTPUT)/sgxsign signing_key.pem $(OUTPUT)/encl.bin > $^ $@ > diff --git a/tools/testing/selftests/x86/sgx/main.c b/tools/testing/selftests/x86/sgx/main.c > index e2265f841fb0..d3e53c71306d 100644 > --- a/tools/testing/selftests/x86/sgx/main.c > +++ b/tools/testing/selftests/x86/sgx/main.c > @@ -1,6 +1,7 @@ > // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) > // Copyright(c) 2016-18 Intel Corporation. > > +#define _GNU_SOURCE > #include <elf.h> > #include <fcntl.h> > #include <stdbool.h> > @@ -9,16 +10,31 @@ > #include <stdlib.h> > #include <string.h> > #include <unistd.h> > +#include <errno.h> > #include <sys/ioctl.h> > #include <sys/mman.h> > #include <sys/stat.h> > -#include <sys/time.h> > +#include <sys/auxv.h> > +#include <signal.h> > +#include <sys/ucontext.h> > + > +#define UNW_LOCAL_ONLY > +#include <libunwind.h> > + > #include "encl_piggy.h" > #include "defines.h" > #include "../../../../../arch/x86/kernel/cpu/sgx/arch.h" > #include "../../../../../arch/x86/include/uapi/asm/sgx.h" > > -static const uint64_t MAGIC = 0x1122334455667788ULL; > +#define _Q(x) __Q(x) > +#define __Q(x) #x > +#define ERRLN "Line " _Q(__LINE__) > + > +#define X86_EFLAGS_TF (1ul << 8) > + > +extern char __image_base[]; > +size_t eenter; > +static size_t vdso_base; > > struct vdso_symtab { > Elf64_Sym *elf_symtab; > @@ -26,20 +42,11 @@ struct vdso_symtab { > Elf64_Word *elf_hashtab; > }; > > -static void *vdso_get_base_addr(char *envp[]) > +static void vdso_init(void) > { > - Elf64_auxv_t *auxv; > - int i; > - > - for (i = 0; envp[i]; i++); > - auxv = (Elf64_auxv_t *)&envp[i + 1]; > - > - for (i = 0; auxv[i].a_type != AT_NULL; i++) { > - if (auxv[i].a_type == AT_SYSINFO_EHDR) > - return (void *)auxv[i].a_un.a_val; > - } > - > - return NULL; > + vdso_base = getauxval(AT_SYSINFO_EHDR); > + if (!vdso_base) > + exit(1); > } The clean up makes sense but should be a separate patch i.e. one logical change per patch. Right now the patch does other mods than the ones explcitly stated in the commit message. I'd suggest open coding vdso_init() to the call site in that patch. Please try to always minimize for diff's. > > static Elf64_Dyn *vdso_get_dyntab(void *addr) > @@ -66,8 +73,9 @@ static void *vdso_get_dyn(void *addr, Elf64_Dyn *dyntab, Elf64_Sxword tag) > return NULL; > } > > -static bool vdso_get_symtab(void *addr, struct vdso_symtab *symtab) > +static bool vdso_get_symtab(struct vdso_symtab *symtab) > { > + void *addr = (void *)vdso_base; > Elf64_Dyn *dyntab = vdso_get_dyntab(addr); > > symtab->elf_symtab = vdso_get_dyn(addr, dyntab, DT_SYMTAB); > @@ -138,7 +146,7 @@ static bool encl_create(int dev_fd, unsigned long bin_size, > base = mmap(NULL, secs->size, PROT_READ | PROT_WRITE | PROT_EXEC, > MAP_SHARED, dev_fd, 0); > if (base == MAP_FAILED) { > - perror("mmap"); > + perror(ERRLN); > return false; > } > > @@ -224,35 +232,271 @@ static bool encl_load(struct sgx_secs *secs, unsigned long bin_size) > return false; > } > > -void sgx_call(void *rdi, void *rsi, void *tcs, > - struct sgx_enclave_exception *exception, > - void *eenter); > +int sgx_call(void *rdi, void *rsi, long rdx, void *rcx, void *r8, void *r9, > + void *tcs, struct sgx_enclave_exinfo *ei, void *cb); > + > +static void show_enclave_exinfo(const struct sgx_enclave_exinfo *exinfop, > + const char *header) > +{ > + static const char * const enclu_leaves[] = { > + "EREPORT", > + "EGETKEY", > + "EENTER", > + "ERESUME", > + "EEXIT" > + }; > + static const char * const exception_names[] = { > + "#DE", > + "#DB", > + "NMI", > + "#BP", > + "#OF", > + "#BR", > + "#UD", > + "#NM", > + "#DF", > + "CSO", > + "#TS", > + "#NP", > + "#SS", > + "#GP", > + "#PF", > + "Unknown", > + "#MF", > + "#AC", > + "#MC", > + "#XM", > + "#VE", > + "Unknown", > + "Unknown", > + "Unknown", > + "Unknown", > + "Unknown", > + "Unknown", > + "Unknown", > + "Unknown", > + "Unknown", > + "Unknown", > + "Unknown" > + }; > + > + printf("%s: leaf:%s(%d)", header, > + enclu_leaves[exinfop->leaf], exinfop->leaf); > + if (exinfop->leaf != 4) > + printf(" trap:%s(%d) ec:%d addr:0x%llx\n", > + exception_names[exinfop->trapnr], exinfop->trapnr, > + exinfop->error_code, exinfop->address); > + else > + printf("\n"); > +} > + > +static const uint64_t MAGIC = 0x1122334455667788ULL; > > -int main(int argc, char *argv[], char *envp[]) > +static void test1(struct sgx_secs *secs) test1, test2 and test3 are not too descriptive names. Every patch should make the code base cleaner, not messier. /Jarkko
On 7/11/2019 8:25 PM, Jarkko Sakkinen wrote: > On Tue, Apr 23, 2019 at 11:26:23PM -0700, Cedric Xing wrote: >> This patch augments SGX selftest with two new tests. >> >> The first test exercises the newly added callback interface, by marking the >> whole enclave range as PROT_READ, then calling mprotect() upon #PFs to add >> necessary PTE permissions per PFEC (#PF Error Code) until the enclave finishes. >> This test also serves as an example to demonstrate the callback interface. >> >> The second test single-steps through __vdso_sgx_enter_enclave() to make sure >> the call stack can be unwound at every instruction within that vDSO API. Its >> purpose is to validate the hand-crafted CFI directives in the assembly. >> >> Signed-off-by: Cedric Xing <cedric.xing@intel.com> >> --- >> tools/testing/selftests/x86/sgx/Makefile | 6 +- >> tools/testing/selftests/x86/sgx/main.c | 323 ++++++++++++++++++--- >> tools/testing/selftests/x86/sgx/sgx_call.S | 40 ++- >> 3 files changed, 322 insertions(+), 47 deletions(-) >> >> diff --git a/tools/testing/selftests/x86/sgx/Makefile b/tools/testing/selftests/x86/sgx/Makefile >> index 3af15d7c8644..31f937e220c4 100644 >> --- a/tools/testing/selftests/x86/sgx/Makefile >> +++ b/tools/testing/selftests/x86/sgx/Makefile >> @@ -14,16 +14,16 @@ TEST_CUSTOM_PROGS := $(OUTPUT)/test_sgx >> all_64: $(TEST_CUSTOM_PROGS) >> >> $(TEST_CUSTOM_PROGS): main.c sgx_call.S $(OUTPUT)/encl_piggy.o >> - $(CC) $(HOST_CFLAGS) -o $@ $^ >> + $(CC) $(HOST_CFLAGS) -o $@ $^ -lunwind -ldl -Wl,--defsym,__image_base=0 -pie >> >> $(OUTPUT)/encl_piggy.o: encl_piggy.S $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss >> $(CC) $(HOST_CFLAGS) -I$(OUTPUT) -c $< -o $@ >> >> $(OUTPUT)/encl.bin: $(OUTPUT)/encl.elf >> - objcopy --remove-section=.got.plt -O binary $< $@ >> + objcopy -O binary $< $@ >> >> $(OUTPUT)/encl.elf: encl.lds encl.c encl_bootstrap.S >> - $(CC) $(ENCL_CFLAGS) -T $^ -o $@ >> + $(CC) $(ENCL_CFLAGS) -T $^ -o $@ -Wl,--build-id=none >> >> $(OUTPUT)/encl.ss: $(OUTPUT)/sgxsign signing_key.pem $(OUTPUT)/encl.bin >> $^ $@ >> diff --git a/tools/testing/selftests/x86/sgx/main.c b/tools/testing/selftests/x86/sgx/main.c >> index e2265f841fb0..d3e53c71306d 100644 >> --- a/tools/testing/selftests/x86/sgx/main.c >> +++ b/tools/testing/selftests/x86/sgx/main.c >> @@ -1,6 +1,7 @@ >> // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) >> // Copyright(c) 2016-18 Intel Corporation. >> >> +#define _GNU_SOURCE >> #include <elf.h> >> #include <fcntl.h> >> #include <stdbool.h> >> @@ -9,16 +10,31 @@ >> #include <stdlib.h> >> #include <string.h> >> #include <unistd.h> >> +#include <errno.h> >> #include <sys/ioctl.h> >> #include <sys/mman.h> >> #include <sys/stat.h> >> -#include <sys/time.h> >> +#include <sys/auxv.h> >> +#include <signal.h> >> +#include <sys/ucontext.h> >> + >> +#define UNW_LOCAL_ONLY >> +#include <libunwind.h> >> + >> #include "encl_piggy.h" >> #include "defines.h" >> #include "../../../../../arch/x86/kernel/cpu/sgx/arch.h" >> #include "../../../../../arch/x86/include/uapi/asm/sgx.h" >> >> -static const uint64_t MAGIC = 0x1122334455667788ULL; >> +#define _Q(x) __Q(x) >> +#define __Q(x) #x >> +#define ERRLN "Line " _Q(__LINE__) >> + >> +#define X86_EFLAGS_TF (1ul << 8) >> + >> +extern char __image_base[]; >> +size_t eenter; >> +static size_t vdso_base; >> >> struct vdso_symtab { >> Elf64_Sym *elf_symtab; >> @@ -26,20 +42,11 @@ struct vdso_symtab { >> Elf64_Word *elf_hashtab; >> }; >> >> -static void *vdso_get_base_addr(char *envp[]) >> +static void vdso_init(void) >> { >> - Elf64_auxv_t *auxv; >> - int i; >> - >> - for (i = 0; envp[i]; i++); >> - auxv = (Elf64_auxv_t *)&envp[i + 1]; >> - >> - for (i = 0; auxv[i].a_type != AT_NULL; i++) { >> - if (auxv[i].a_type == AT_SYSINFO_EHDR) >> - return (void *)auxv[i].a_un.a_val; >> - } >> - >> - return NULL; >> + vdso_base = getauxval(AT_SYSINFO_EHDR); >> + if (!vdso_base) >> + exit(1); >> } > > The clean up makes sense but should be a separate patch i.e. one > logical change per patch. Right now the patch does other mods > than the ones explcitly stated in the commit message. > > I'd suggest open coding vdso_init() to the call site in that > patch. > > Please try to always minimize for diff's. > >> >> static Elf64_Dyn *vdso_get_dyntab(void *addr) >> @@ -66,8 +73,9 @@ static void *vdso_get_dyn(void *addr, Elf64_Dyn *dyntab, Elf64_Sxword tag) >> return NULL; >> } >> >> -static bool vdso_get_symtab(void *addr, struct vdso_symtab *symtab) >> +static bool vdso_get_symtab(struct vdso_symtab *symtab) >> { >> + void *addr = (void *)vdso_base; >> Elf64_Dyn *dyntab = vdso_get_dyntab(addr); >> >> symtab->elf_symtab = vdso_get_dyn(addr, dyntab, DT_SYMTAB); >> @@ -138,7 +146,7 @@ static bool encl_create(int dev_fd, unsigned long bin_size, >> base = mmap(NULL, secs->size, PROT_READ | PROT_WRITE | PROT_EXEC, >> MAP_SHARED, dev_fd, 0); >> if (base == MAP_FAILED) { >> - perror("mmap"); >> + perror(ERRLN); >> return false; >> } >> >> @@ -224,35 +232,271 @@ static bool encl_load(struct sgx_secs *secs, unsigned long bin_size) >> return false; >> } >> >> -void sgx_call(void *rdi, void *rsi, void *tcs, >> - struct sgx_enclave_exception *exception, >> - void *eenter); >> +int sgx_call(void *rdi, void *rsi, long rdx, void *rcx, void *r8, void *r9, >> + void *tcs, struct sgx_enclave_exinfo *ei, void *cb); >> + >> +static void show_enclave_exinfo(const struct sgx_enclave_exinfo *exinfop, >> + const char *header) >> +{ >> + static const char * const enclu_leaves[] = { >> + "EREPORT", >> + "EGETKEY", >> + "EENTER", >> + "ERESUME", >> + "EEXIT" >> + }; >> + static const char * const exception_names[] = { >> + "#DE", >> + "#DB", >> + "NMI", >> + "#BP", >> + "#OF", >> + "#BR", >> + "#UD", >> + "#NM", >> + "#DF", >> + "CSO", >> + "#TS", >> + "#NP", >> + "#SS", >> + "#GP", >> + "#PF", >> + "Unknown", >> + "#MF", >> + "#AC", >> + "#MC", >> + "#XM", >> + "#VE", >> + "Unknown", >> + "Unknown", >> + "Unknown", >> + "Unknown", >> + "Unknown", >> + "Unknown", >> + "Unknown", >> + "Unknown", >> + "Unknown", >> + "Unknown", >> + "Unknown" >> + }; >> + >> + printf("%s: leaf:%s(%d)", header, >> + enclu_leaves[exinfop->leaf], exinfop->leaf); >> + if (exinfop->leaf != 4) >> + printf(" trap:%s(%d) ec:%d addr:0x%llx\n", >> + exception_names[exinfop->trapnr], exinfop->trapnr, >> + exinfop->error_code, exinfop->address); >> + else >> + printf("\n"); >> +} >> + >> +static const uint64_t MAGIC = 0x1122334455667788ULL; >> >> -int main(int argc, char *argv[], char *envp[]) >> +static void test1(struct sgx_secs *secs) > > test1, test2 and test3 are not too descriptive names. Every patch should > make the code base cleaner, not messier. I don't think it that important, as there are many test## occurrences in existing selftests. Anyway, I've added comments to those test functions to brief what is done. Hope you'll find them helpful. > /Jarkko >
diff --git a/tools/testing/selftests/x86/sgx/Makefile b/tools/testing/selftests/x86/sgx/Makefile index 3af15d7c8644..31f937e220c4 100644 --- a/tools/testing/selftests/x86/sgx/Makefile +++ b/tools/testing/selftests/x86/sgx/Makefile @@ -14,16 +14,16 @@ TEST_CUSTOM_PROGS := $(OUTPUT)/test_sgx all_64: $(TEST_CUSTOM_PROGS) $(TEST_CUSTOM_PROGS): main.c sgx_call.S $(OUTPUT)/encl_piggy.o - $(CC) $(HOST_CFLAGS) -o $@ $^ + $(CC) $(HOST_CFLAGS) -o $@ $^ -lunwind -ldl -Wl,--defsym,__image_base=0 -pie $(OUTPUT)/encl_piggy.o: encl_piggy.S $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss $(CC) $(HOST_CFLAGS) -I$(OUTPUT) -c $< -o $@ $(OUTPUT)/encl.bin: $(OUTPUT)/encl.elf - objcopy --remove-section=.got.plt -O binary $< $@ + objcopy -O binary $< $@ $(OUTPUT)/encl.elf: encl.lds encl.c encl_bootstrap.S - $(CC) $(ENCL_CFLAGS) -T $^ -o $@ + $(CC) $(ENCL_CFLAGS) -T $^ -o $@ -Wl,--build-id=none $(OUTPUT)/encl.ss: $(OUTPUT)/sgxsign signing_key.pem $(OUTPUT)/encl.bin $^ $@ diff --git a/tools/testing/selftests/x86/sgx/main.c b/tools/testing/selftests/x86/sgx/main.c index e2265f841fb0..d3e53c71306d 100644 --- a/tools/testing/selftests/x86/sgx/main.c +++ b/tools/testing/selftests/x86/sgx/main.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) // Copyright(c) 2016-18 Intel Corporation. +#define _GNU_SOURCE #include <elf.h> #include <fcntl.h> #include <stdbool.h> @@ -9,16 +10,31 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <errno.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/stat.h> -#include <sys/time.h> +#include <sys/auxv.h> +#include <signal.h> +#include <sys/ucontext.h> + +#define UNW_LOCAL_ONLY +#include <libunwind.h> + #include "encl_piggy.h" #include "defines.h" #include "../../../../../arch/x86/kernel/cpu/sgx/arch.h" #include "../../../../../arch/x86/include/uapi/asm/sgx.h" -static const uint64_t MAGIC = 0x1122334455667788ULL; +#define _Q(x) __Q(x) +#define __Q(x) #x +#define ERRLN "Line " _Q(__LINE__) + +#define X86_EFLAGS_TF (1ul << 8) + +extern char __image_base[]; +size_t eenter; +static size_t vdso_base; struct vdso_symtab { Elf64_Sym *elf_symtab; @@ -26,20 +42,11 @@ struct vdso_symtab { Elf64_Word *elf_hashtab; }; -static void *vdso_get_base_addr(char *envp[]) +static void vdso_init(void) { - Elf64_auxv_t *auxv; - int i; - - for (i = 0; envp[i]; i++); - auxv = (Elf64_auxv_t *)&envp[i + 1]; - - for (i = 0; auxv[i].a_type != AT_NULL; i++) { - if (auxv[i].a_type == AT_SYSINFO_EHDR) - return (void *)auxv[i].a_un.a_val; - } - - return NULL; + vdso_base = getauxval(AT_SYSINFO_EHDR); + if (!vdso_base) + exit(1); } static Elf64_Dyn *vdso_get_dyntab(void *addr) @@ -66,8 +73,9 @@ static void *vdso_get_dyn(void *addr, Elf64_Dyn *dyntab, Elf64_Sxword tag) return NULL; } -static bool vdso_get_symtab(void *addr, struct vdso_symtab *symtab) +static bool vdso_get_symtab(struct vdso_symtab *symtab) { + void *addr = (void *)vdso_base; Elf64_Dyn *dyntab = vdso_get_dyntab(addr); symtab->elf_symtab = vdso_get_dyn(addr, dyntab, DT_SYMTAB); @@ -138,7 +146,7 @@ static bool encl_create(int dev_fd, unsigned long bin_size, base = mmap(NULL, secs->size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, dev_fd, 0); if (base == MAP_FAILED) { - perror("mmap"); + perror(ERRLN); return false; } @@ -224,35 +232,271 @@ static bool encl_load(struct sgx_secs *secs, unsigned long bin_size) return false; } -void sgx_call(void *rdi, void *rsi, void *tcs, - struct sgx_enclave_exception *exception, - void *eenter); +int sgx_call(void *rdi, void *rsi, long rdx, void *rcx, void *r8, void *r9, + void *tcs, struct sgx_enclave_exinfo *ei, void *cb); + +static void show_enclave_exinfo(const struct sgx_enclave_exinfo *exinfop, + const char *header) +{ + static const char * const enclu_leaves[] = { + "EREPORT", + "EGETKEY", + "EENTER", + "ERESUME", + "EEXIT" + }; + static const char * const exception_names[] = { + "#DE", + "#DB", + "NMI", + "#BP", + "#OF", + "#BR", + "#UD", + "#NM", + "#DF", + "CSO", + "#TS", + "#NP", + "#SS", + "#GP", + "#PF", + "Unknown", + "#MF", + "#AC", + "#MC", + "#XM", + "#VE", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown" + }; + + printf("%s: leaf:%s(%d)", header, + enclu_leaves[exinfop->leaf], exinfop->leaf); + if (exinfop->leaf != 4) + printf(" trap:%s(%d) ec:%d addr:0x%llx\n", + exception_names[exinfop->trapnr], exinfop->trapnr, + exinfop->error_code, exinfop->address); + else + printf("\n"); +} + +static const uint64_t MAGIC = 0x1122334455667788ULL; -int main(int argc, char *argv[], char *envp[]) +static void test1(struct sgx_secs *secs) +{ + uint64_t result = 0; + struct sgx_enclave_exinfo exinfo; + + printf("[1] Entering the enclave without callback.\n"); + + printf("Input: 0x%lx\n Expect: Same as input\n", MAGIC); + sgx_call((void *)&MAGIC, &result, 0, NULL, NULL, NULL, + (void *)secs->base, &exinfo, NULL); + show_enclave_exinfo(&exinfo, " Exit"); + if (result != MAGIC) { + fprintf(stderr, "0x%lx != 0x%lx\n", result, MAGIC); + exit(1); + } + printf(" Output: 0x%lx\n", result); + + printf("Input: Null TCS\n Expect: #PF at EENTER\n"); + sgx_call((void *)&MAGIC, &result, 0, NULL, NULL, NULL, + NULL, &exinfo, NULL); + show_enclave_exinfo(&exinfo, " Exit"); + if (exinfo.leaf != 2 /*EENTER*/ || exinfo.trapnr != 14 /*#PF*/) + exit(1); +} + +static int test2_callback(long rdi, long rsi, long rdx, + struct sgx_enclave_exinfo *ei, long r8, long r9, + void *tcs, long ursp) +{ + show_enclave_exinfo(ei, " callback"); + + switch (ei->leaf) { + case 4: + return 0; + case 3: + case 2: + switch (ei->trapnr) { + case 1: /*#DB*/ + break; + case 14:/*#PF*/ + if ((ei->error_code & 1) == 0) { + fprintf(stderr, ERRLN + ": Unexpected #PF error code\n"); + exit(1); + } + if (mprotect((void *)(ei->address & -0x1000), 0x1000, + ((ei->error_code & 2) ? PROT_WRITE : 0) | + ((ei->error_code & 0x10) ? PROT_EXEC : 0) | + PROT_READ)) { + perror(ERRLN); + exit(1); + } + break; + default: + fprintf(stderr, ERRLN ": Unexpected exception\n"); + exit(1); + } + return ei->leaf == 2 ? -EAGAIN : ei->leaf; + } + return -EINVAL; +} + +static void test2(struct sgx_secs *secs) +{ + uint64_t result = 0; + struct sgx_enclave_exinfo exinfo; + + printf("[2] Entering the enclave with callback.\n"); + + printf("Input: 0x%lx\n Expect: Same as input\n", MAGIC); + sgx_call((void *)&MAGIC, &result, 0, NULL, NULL, NULL, + (void *)secs->base, &exinfo, test2_callback); + if (result != MAGIC) { + fprintf(stderr, "0x%lx != 0x%lx\n", result, MAGIC); + exit(1); + } + printf(" Output: 0x%lx\n", result); + + printf("Input: Read-only enclave (0x%lx-0x%lx)\n" + " Expect: #PFs to be fixed by callback\n", + secs->base, secs->base + (encl_bin_end - encl_bin) - 1); + if (mprotect((void *)secs->base, encl_bin_end - encl_bin, PROT_READ)) { + perror(ERRLN); + exit(1); + } + while (sgx_call((void *)&MAGIC, &result, 0, NULL, NULL, NULL, + (void *)secs->base, &exinfo, test2_callback) == -EAGAIN) + ; + show_enclave_exinfo(&exinfo, " Exit"); + if (exinfo.leaf != 4 /*EEXIT*/) + exit(1); +} + +static struct test3_proc_context { + unw_word_t ip, bx, sp, bp, r12, r13, r14, r15; +} test3_ctx; + +static unw_word_t test3_getcontext(unw_cursor_t *cursor, + struct test3_proc_context *ctxp) +{ + unw_get_reg(cursor, UNW_REG_IP, &ctxp->ip); + unw_get_reg(cursor, UNW_REG_SP, &ctxp->sp); + unw_get_reg(cursor, UNW_X86_64_RBX, &ctxp->bx); + unw_get_reg(cursor, UNW_X86_64_RBP, &ctxp->bp); + unw_get_reg(cursor, UNW_X86_64_R12, &ctxp->r12); + unw_get_reg(cursor, UNW_X86_64_R13, &ctxp->r13); + unw_get_reg(cursor, UNW_X86_64_R14, &ctxp->r14); + unw_get_reg(cursor, UNW_X86_64_R15, &ctxp->r15); + return ctxp->ip; +} + +static void test3_sigtrap(int sig, siginfo_t *info, ucontext_t *ctxp) +{ + static int in_vdso_eenter; + static size_t caller; + + unw_cursor_t cursor; + unw_context_t uc; + struct test3_proc_context pc; + + if (ctxp->uc_mcontext.gregs[REG_RIP] == eenter) { + in_vdso_eenter = 1; + caller = *(size_t *)(ctxp->uc_mcontext.gregs[REG_RSP]); + printf(" trace started at ip:%llx (vdso:0x%llx)\n", + ctxp->uc_mcontext.gregs[REG_RIP], + ctxp->uc_mcontext.gregs[REG_RIP] - vdso_base); + } + + if (!in_vdso_eenter) + return; + + if (ctxp->uc_mcontext.gregs[REG_RIP] == caller) { + in_vdso_eenter = 0; + ctxp->uc_mcontext.gregs[REG_EFL] &= ~X86_EFLAGS_TF; + printf(" trace ended successfully at ip:%llx (executable:0x%llx)\n", + ctxp->uc_mcontext.gregs[REG_RIP], + ctxp->uc_mcontext.gregs[REG_RIP] - + (size_t)__image_base); + return; + } + + unw_getcontext(&uc); + unw_init_local(&cursor, &uc); + while (unw_step(&cursor) > 0 && + test3_getcontext(&cursor, &pc) != test3_ctx.ip) + ; + + if (memcmp(&pc, &test3_ctx, sizeof(pc))) { + fprintf(stderr, ERRLN ": Error unwinding\n"); + exit(1); + } +} + +static void test3_set_tf(void) +{ + __asm__ ("pushfq; orl %0, (%%rsp); popfq" : : "i"(X86_EFLAGS_TF)); +} + +static void test3(struct sgx_secs *secs) +{ + unw_cursor_t cursor; + unw_context_t uc; + struct sigaction sa = { + .sa_sigaction = (void (*)(int, siginfo_t*, void*))test3_sigtrap, + .sa_flags = SA_SIGINFO, + }; + + unw_getcontext(&uc); + unw_init_local(&cursor, &uc); + if (unw_step(&cursor) > 0) + test3_getcontext(&cursor, &test3_ctx); + else { + fprintf(stderr, ERRLN ": error initializing unwind context\n"); + exit(1); + } + + if (sigaction(SIGTRAP, &sa, NULL) < 0) { + perror(ERRLN); + exit(1); + } + + test3_set_tf(); + test1(secs); + + test3_set_tf(); + test2(secs); +} + +int main(void) { unsigned long bin_size = encl_bin_end - encl_bin; unsigned long ss_size = encl_ss_end - encl_ss; - struct sgx_enclave_exception exception; Elf64_Sym *eenter_sym; struct vdso_symtab symtab; struct sgx_secs secs; - uint64_t result = 0; - void *eenter; - void *addr; - - memset(&exception, 0, sizeof(exception)); - addr = vdso_get_base_addr(envp); - if (!addr) - exit(1); + vdso_init(); - if (!vdso_get_symtab(addr, &symtab)) + if (!vdso_get_symtab(&symtab)) exit(1); eenter_sym = vdso_symtab_get(&symtab, "__vdso_sgx_enter_enclave"); if (!eenter_sym) exit(1); - eenter = addr + eenter_sym->st_value; + eenter = vdso_base + eenter_sym->st_value; printf("Binary size %lu (0x%lx), SIGSTRUCT size %lu\n", bin_size, bin_size, ss_size); @@ -266,14 +510,11 @@ int main(int argc, char *argv[], char *envp[]) if (!encl_load(&secs, bin_size)) exit(1); - printf("Input: 0x%lx\n", MAGIC); - sgx_call((void *)&MAGIC, &result, (void *)secs.base, &exception, - eenter); - if (result != MAGIC) { - fprintf(stderr, "0x%lx != 0x%lx\n", result, MAGIC); - exit(1); - } + printf("--- Functional Tests ---\n"); + test1(&secs); + test2(&secs); - printf("Output: 0x%lx\n", result); - exit(0); + printf("--- Unwind Tests ---\n"); + test3(&secs); + return 0; } diff --git a/tools/testing/selftests/x86/sgx/sgx_call.S b/tools/testing/selftests/x86/sgx/sgx_call.S index 14bd0a044199..ca2c0c947758 100644 --- a/tools/testing/selftests/x86/sgx/sgx_call.S +++ b/tools/testing/selftests/x86/sgx/sgx_call.S @@ -7,9 +7,43 @@ .global sgx_call sgx_call: + .cfi_startproc + push %r15 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r15, 0 + push %r14 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r14, 0 + push %r13 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r13, 0 + push %r12 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r12, 0 push %rbx - mov $0x02, %rax - mov %rdx, %rbx - call *%r8 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rbx, 0 + push $0 + .cfi_adjust_cfa_offset 8 + push 0x48(%rsp) + .cfi_adjust_cfa_offset 8 + push 0x48(%rsp) + .cfi_adjust_cfa_offset 8 + push 0x48(%rsp) + .cfi_adjust_cfa_offset 8 + mov $2, %eax + call *eenter(%rip) + add $0x20, %rsp + .cfi_adjust_cfa_offset -0x20 pop %rbx + .cfi_adjust_cfa_offset -8 + pop %r12 + .cfi_adjust_cfa_offset -8 + pop %r13 + .cfi_adjust_cfa_offset -8 + pop %r14 + .cfi_adjust_cfa_offset -8 + pop %r15 + .cfi_adjust_cfa_offset -8 ret + .cfi_endproc
This patch augments SGX selftest with two new tests. The first test exercises the newly added callback interface, by marking the whole enclave range as PROT_READ, then calling mprotect() upon #PFs to add necessary PTE permissions per PFEC (#PF Error Code) until the enclave finishes. This test also serves as an example to demonstrate the callback interface. The second test single-steps through __vdso_sgx_enter_enclave() to make sure the call stack can be unwound at every instruction within that vDSO API. Its purpose is to validate the hand-crafted CFI directives in the assembly. Signed-off-by: Cedric Xing <cedric.xing@intel.com> --- tools/testing/selftests/x86/sgx/Makefile | 6 +- tools/testing/selftests/x86/sgx/main.c | 323 ++++++++++++++++++--- tools/testing/selftests/x86/sgx/sgx_call.S | 40 ++- 3 files changed, 322 insertions(+), 47 deletions(-)