diff --git a/Pal/src/host/Linux-SGX/ecall_types.h b/Pal/src/host/Linux-SGX/ecall_types.h index 437cd98255..f8c1dc726c 100644 --- a/Pal/src/host/Linux-SGX/ecall_types.h +++ b/Pal/src/host/Linux-SGX/ecall_types.h @@ -4,6 +4,9 @@ enum { ECALL_ENCLAVE_START = 0, ECALL_THREAD_START, + ECALL_STACK_EXPAND, + ECALL_THREAD_SETUP, + ECALL_THREAD_CREATE, ECALL_NR, }; diff --git a/Pal/src/host/Linux-SGX/enclave_ecalls.c b/Pal/src/host/Linux-SGX/enclave_ecalls.c index 82bacd7749..48b55fe371 100644 --- a/Pal/src/host/Linux-SGX/enclave_ecalls.c +++ b/Pal/src/host/Linux-SGX/enclave_ecalls.c @@ -17,6 +17,111 @@ void pal_start_thread (void); extern void * enclave_base, * enclave_top; +struct thread_map { + unsigned int tid; + unsigned int thread_index; + unsigned int status; + sgx_arch_tcs_t * tcs; + unsigned long tcs_addr; + unsigned long ssa_addr; + unsigned long tls_addr; + unsigned long aux_stack_addr; /* only applicable to EDMM */ + unsigned long enclave_entry; +}; + +/* pal_expand_stack grows the stack dynamically under EDMM mode, + * the growing strategy is (1) commit EPC pages to the space between + * fault address and the current stack top; (2) commit one more EPC + * page below the fault address for future stack grow + + * fault_addr: the address where causing #PF by push instructions + */ +void pal_expand_stack(unsigned long fault_addr) +{ + unsigned long stack_commit_top = GET_ENCLAVE_TLS(stack_commit_top); + unsigned long accept_flags = SGX_SECINFO_FLAGS_R | SGX_SECINFO_FLAGS_W | + SGX_SECINFO_FLAGS_REG | SGX_SECINFO_FLAGS_PENDING; + unsigned long stack_init_addr = GET_ENCLAVE_TLS(initial_stack_offset); + unsigned long end_addr = fault_addr - PRESET_PAGESIZE; + + SGX_DBG(DBG_M, "fault_addr, stack_commit_top, stack_init_addr: %p, %p, %p\n", + fault_addr, stack_commit_top, stack_init_addr); + if (fault_addr < (stack_init_addr - ENCLAVE_STACK_SIZE * PRESET_PAGESIZE)) { + SGX_DBG(DBG_E, "stack overrun, stop!\n"); + return ; + } + /* Bridge the gap between fault addr and top if any */ + sgx_accept_pages(accept_flags, fault_addr, stack_commit_top, 0); + + stack_commit_top = fault_addr; + + /* Overgrow one more page */ + if (end_addr >= stack_init_addr - ENCLAVE_STACK_SIZE * PRESET_PAGESIZE) { + sgx_accept_pages(accept_flags, end_addr, fault_addr, 0); + stack_commit_top = fault_addr; + } + +} + +/* This function setup the pages necessary for runing a thread including: + * (1) SSAs (2) TLS (3)TCS (4) Stack + * ecall_args: pointer to the thread-dependent information for setup the new thread + */ +void pal_thread_setup(void * ecall_args){ + struct thread_map * thread_info = (struct thread_map *)ecall_args; + unsigned long regular_flags = SGX_SECINFO_FLAGS_R | SGX_SECINFO_FLAGS_W | + SGX_SECINFO_FLAGS_REG | SGX_SECINFO_FLAGS_PENDING; + SGX_DBG(DBG_M, "the created thread using tcs at %p, tls at %p, ssa at %p\n", + thread_info->tcs_addr, thread_info->tls_addr, thread_info->ssa_addr); + sgx_accept_pages(regular_flags, thread_info->tcs_addr, thread_info->tcs_addr + PRESET_PAGESIZE, 0); + sgx_accept_pages(regular_flags, thread_info->tls_addr, thread_info->tls_addr + PRESET_PAGESIZE, 0); + sgx_accept_pages(regular_flags, thread_info->ssa_addr, thread_info->ssa_addr + 2 * PRESET_PAGESIZE, 0); + + // Setup TLS + struct enclave_tls* tls = (struct enclave_tls *) thread_info->tls_addr; + tls->enclave_size = GET_ENCLAVE_TLS(enclave_size); + tls->tcs_offset = thread_info->tcs_addr; + + unsigned long stack_gap = thread_info->thread_index * (ENCLAVE_STACK_SIZE + PRESET_PAGESIZE); // There is a gap between stacks + tls->initial_stack_offset = GET_ENCLAVE_TLS(initial_stack_offset) - stack_gap; + + tls->ssa = (void *)thread_info->ssa_addr; + tls->gpr = tls->ssa + PRESET_PAGESIZE - sizeof(sgx_arch_gpr_t); + tls->aux_stack_offset = thread_info->aux_stack_addr; + tls->stack_commit_top = tls->initial_stack_offset; + tls->ocall_pending = 0; + + // Setup TCS + thread_info->tcs = (sgx_arch_tcs_t *) thread_info->tcs_addr; + memset((void*)thread_info->tcs_addr, 0, PRESET_PAGESIZE); + thread_info->tcs->ossa = thread_info->ssa_addr; + thread_info->tcs->nssa = 2; + thread_info->tcs->oentry = thread_info->enclave_entry; + thread_info->tcs->ofsbasgx = 0; + thread_info->tcs->ogsbasgx = thread_info->tls_addr; + thread_info->tcs->fslimit = 0xfff; + thread_info->tcs->gslimit = 0xfff; + + // PRE-ALLOCATE two pages for STACK + unsigned long accept_flags = SGX_SECINFO_FLAGS_R | SGX_SECINFO_FLAGS_W | + SGX_SECINFO_FLAGS_REG | SGX_SECINFO_FLAGS_PENDING; + + sgx_accept_pages(accept_flags, tls->initial_stack_offset - 2 * PRESET_PAGESIZE, tls->initial_stack_offset, 0); +} + +/* pal_thread_create finalizes the creataion of thread by changing + * the type of tcs page from regular to TCS + * ecall_args: the tcs page address to be TCS type + */ +void pal_thread_create(void * ecall_args){ + struct thread_map * thread_info = (struct thread_map *)ecall_args; + unsigned long tcs_flags = SGX_SECINFO_FLAGS_TCS | SGX_SECINFO_FLAGS_MODIFIED; + + int rs = sgx_accept_pages(tcs_flags, thread_info->tcs_addr, thread_info->tcs_addr + PRESET_PAGESIZE, 0); + if (rs != 0) SGX_DBG(DBG_E, "EACCEPT TCS Change failed: %d\n", rs); +} + +/* handle_ecall is the main entry of all ecall functions */ int handle_ecall (long ecall_index, void * ecall_args, void * exit_target, void * untrusted_stack, void * enclave_base_addr) { @@ -37,7 +142,8 @@ int handle_ecall (long ecall_index, void * ecall_args, void * exit_target, SET_ENCLAVE_TLS(exit_target, exit_target); SET_ENCLAVE_TLS(ustack_top, untrusted_stack); SET_ENCLAVE_TLS(ustack, untrusted_stack); - + SET_ENCLAVE_TLS(ocall_pending, 0); + switch(ecall_index) { case ECALL_ENCLAVE_START: { ms_ecall_enclave_start_t * ms = @@ -47,14 +153,27 @@ int handle_ecall (long ecall_index, void * ecall_args, void * exit_target, pal_linux_main(ms->ms_arguments, ms->ms_environments, ms->ms_sec_info); + ocall_exit(); break; } case ECALL_THREAD_START: pal_start_thread(); + ocall_exit(); break; + case ECALL_STACK_EXPAND: + pal_expand_stack((unsigned long)ecall_args); + break; + case ECALL_THREAD_SETUP: + pal_thread_setup(ecall_args); + break; + case ECALL_THREAD_CREATE: + pal_thread_create(ecall_args); + break; + default: + SGX_DBG(DBG_E, "Ecall error, invalid ecall index!\n"); + ocall_exit(); } - - ocall_exit(); + return 0; } diff --git a/Pal/src/host/Linux-SGX/enclave_entry.S b/Pal/src/host/Linux-SGX/enclave_entry.S index 0d4b25b6ec..1ec36f103b 100644 --- a/Pal/src/host/Linux-SGX/enclave_entry.S +++ b/Pal/src/host/Linux-SGX/enclave_entry.S @@ -13,10 +13,19 @@ enclave_entry: # current SSA is in RAX (Trusted) cmp $0, %rax - jne .Lhandle_resume + je .Lnormal_enter + + # Exception for growing stacks + cmp $2, %rdi + je .Lnormal_enter + # Not OCALL made in Exception context + cmp $1, %gs:SGX_OCALL_PENDING + jne .Lhandle_resume + # TCS is in RBX (Trusted) +.Lnormal_enter: # AEP address in RCX (Trusted) mov %rcx, %gs:SGX_AEP @@ -35,9 +44,7 @@ enclave_entry: # from a OCALL in the untrusted PAL. Attackers can manipulate RDI # to deceive the trusted PAL. - # A safe design: check if %gs:SGX_EXIT_TARGET is ever assigned - mov %gs:SGX_EXIT_TARGET, %rcx - cmp $0, %rcx + cmp $0, %gs:SGX_OCALL_PENDING jne .Lreturn_from_ocall # PAL convention: @@ -54,10 +61,22 @@ enclave_entry: # push untructed stack address to RCX mov %rsp, %rcx + # handle stack grow, using auxiliary stack + cmp $2, %rdi + je .Lhandle_aux_stack + + # setup thread context, using auxiliary stack + cmp $3, %rdi + je .Lhandle_aux_stack + + cmp $4, %rdi + je .Lhandle_aux_stack + # switch to enclve stack: enclave base + %gs:SGX_INITIAL_STACK_OFFSET add %gs:SGX_INITIAL_STACK_OFFSET, %rbx mov %rbx, %rsp +.Ldo_handle_ecall: # clear the rest of register states xor %rax, %rax xor %rbx, %rbx @@ -72,12 +91,25 @@ enclave_entry: # register states need to be carefully checked, so we move the handling # to handle_ecall() in enclave_ecalls.c call handle_ecall - - # never return to this point (should die) + + # ecall_enclave_start/ecall_thread_start never return to this point + # other ecalls return here xor %rdi, %rdi xor %rsi, %rsi jmp .Leexit +.Lhandle_aux_stack: + add %gs:SGX_AUX_STACK_OFFSET, %rbx + mov %rbx, %rsp + + # In exception handler, ecall returns to the next + # RCX contains the return address, however occupied + # Using SGX_AEP instead + + mov %gs:SGX_AEP, %r9 + mov %r9, %gs:SGX_ECALL_RET_TARGET + jmp .Ldo_handle_ecall + .Lhandle_resume: # PAL convention: # RDI - external event @@ -233,7 +265,9 @@ sgx_ocall: push %rbp mov %rsp, %gs:SGX_STACK - + + # ocall sets ocall_pending before exit + movq $1, %gs:SGX_OCALL_PENDING jmp .Leexit .Lexception_handler: @@ -251,13 +285,24 @@ sgx_ocall: xor %rbp, %rbp mov %gs:SGX_USTACK, %rsp - and $STACK_ALIGN, %rsp + # If it's an ecall return using + # ecall_ret_target as exit target + cmp $1, %gs:SGX_OCALL_PENDING + jne .Lecall_return_setup + mov %gs:SGX_EXIT_TARGET, %rbx mov %gs:SGX_AEP, %rcx + +.Lexecute_exit: mov $EEXIT, %rax ENCLU +.Lecall_return_setup: + mov %gs:SGX_ECALL_RET_TARGET, %rbx + mov %gs:SGX_AEP, %rcx + jmp .Lexecute_exit + .Lreturn_from_ocall: # PAL convention: # RDI - return value @@ -265,6 +310,9 @@ sgx_ocall: mov %rdi, %rax + # clear ocall_pending + movq $0, %gs:SGX_OCALL_PENDING + # restore FSBASE if necessary mov %gs:SGX_FSBASE, %rbx cmp $0, %rbx @@ -353,6 +401,52 @@ sgx_getkey: .cfi_endproc .size sgx_getkey, .-sgx_getkey +/* + * sgx_accept: + * EACCEPT pages for dynamic memory management + */ + .global sgx_accept + .type sgx_accept, @function + +sgx_accept: + .cfi_startproc + + push %rbx + push %rcx + mov %rdi, %rbx + mov %rsi, %rcx + mov $EACCEPT, %rax + ENCLU + pop %rcx + pop %rbx + ret + + .cfi_endproc + .size sgx_accept, .-sgx_accept + +/* + * sgx_modpe: + * EMODPE pages for dynamic memory management + */ + .global sgx_modpe + .type sgx_modpe, @function + +sgx_modpe: + .cfi_startproc + + push %rbx + push %rcx + mov %rdi, %rbx + mov %rsi, %rcx + mov $EMODPE, %rax + ENCLU + pop %rcx + pop %rbx + ret + + .cfi_endproc + .size sgx_modpe, .-sgx_modpe + /* * rdrand: * Get hardware generated random value. diff --git a/Pal/src/host/Linux-SGX/enclave_framework.c b/Pal/src/host/Linux-SGX/enclave_framework.c index 618efb8e68..7be2e83ce3 100644 --- a/Pal/src/host/Linux-SGX/enclave_framework.c +++ b/Pal/src/host/Linux-SGX/enclave_framework.c @@ -97,6 +97,40 @@ int sgx_verify_report (sgx_arch_report_t * report) return 0; } + +#define SE_DECLSPEC_ALIGN(x) __attribute__((aligned(x))) + +/* sgx_accept_pages do EACCEPT on the pages from address lo to address hi */ +int sgx_accept_pages(uint64_t sfl, size_t lo, size_t hi, bool executable) +{ + size_t addr = hi; + SE_DECLSPEC_ALIGN(sizeof(sgx_arch_secinfo_t)) sgx_arch_secinfo_t si; + si.flags = sfl; + + for (uint16_t i = 0; i < (sizeof(si.reserved)/sizeof(si.reserved[0])); i++) + si.reserved[i] = 0; + + SGX_DBG(DBG_M, "sgx_accept_pages: %p - %p, executable: %d \n", lo, hi, executable); + SE_DECLSPEC_ALIGN(sizeof(sgx_arch_secinfo_t)) sgx_arch_secinfo_t smi = si; + smi.flags |= SGX_SECINFO_FLAGS_X; + + while (lo < addr) + { + addr -= PRESET_PAGESIZE; + int rc = sgx_accept(&si, (const void *)addr); + + /* FIXME: Need a better handle here, adding the flow for checking multiple EACCEPT on the same page */ + if (rc != 0) { +// SGX_DBG(DBG_E, "eaccept fails: %d\n", rc); +// return rc; + continue; + } + if (executable) + rc = sgx_modpe(&smi, (const void *)addr); + } + return 0; +} + int init_enclave_key (void) { sgx_arch_keyrequest_t keyrequest; diff --git a/Pal/src/host/Linux-SGX/enclave_pages.c b/Pal/src/host/Linux-SGX/enclave_pages.c index 75f33b4a12..0726581141 100644 --- a/Pal/src/host/Linux-SGX/enclave_pages.c +++ b/Pal/src/host/Linux-SGX/enclave_pages.c @@ -70,6 +70,16 @@ static void assert_vma_list (void) #endif } +void allocate_page_range(void * addr, uint64_t size) +{ + uint64_t start_addr = (uint64_t)addr; + uint64_t end_addr = (uint64_t)addr + size; + uint64_t accept_flags = SGX_SECINFO_FLAGS_R | SGX_SECINFO_FLAGS_W | + SGX_SECINFO_FLAGS_REG | SGX_SECINFO_FLAGS_PENDING; + + sgx_accept_pages(accept_flags, start_addr, end_addr, 1); +} + // TODO: This function should be fixed to always either return exactly `addr` or // fail. void * get_reserved_pages(void * addr, uint64_t size) @@ -158,6 +168,10 @@ void * get_reserved_pages(void * addr, uint64_t size) listp_first_entry(&heap_vma_list, struct heap_vma, list); } + /* Dynamically Request EPC pages in EDMM Mode */ + if (pal_sec.edmm_mode) + allocate_page_range (addr, size); + if (prev && next) SGX_DBG(DBG_M, "insert vma between %p-%p and %p-%p\n", next->bottom, next->top, prev->bottom, prev->top); diff --git a/Pal/src/host/Linux-SGX/pal_linux.h b/Pal/src/host/Linux-SGX/pal_linux.h index 1c95eed06c..de2b785d80 100644 --- a/Pal/src/host/Linux-SGX/pal_linux.h +++ b/Pal/src/host/Linux-SGX/pal_linux.h @@ -61,6 +61,13 @@ extern struct pal_linux_state { #define PRESET_PAGESIZE (1 << 12) +#define MAX_THREAD_NUM 16 + +#define AUX_STACK_SIZE PRESET_PAGESIZE * 2 + +/* 512B per thread for using auxiliary stack */ +#define AUX_STACK_SIZE_PER_THREAD AUX_STACK_SIZE / MAX_THREAD_NUM + #define DEFAULT_BACKLOG 2048 static inline int HOST_FLAGS (int alloc_type, int prot) diff --git a/Pal/src/host/Linux-SGX/pal_security.h b/Pal/src/host/Linux-SGX/pal_security.h index 6dd5195c7c..0dfbf2cfdb 100644 --- a/Pal/src/host/Linux-SGX/pal_security.h +++ b/Pal/src/host/Linux-SGX/pal_security.h @@ -39,6 +39,9 @@ struct pal_sec { sgx_arch_hash_t mrsigner; sgx_arch_attributes_t enclave_attributes; + /* edmm mode */ + uint8_t edmm_mode; + /* remaining heap usable by application */ PAL_PTR heap_min, heap_max; diff --git a/Pal/src/host/Linux-SGX/sgx_api.h b/Pal/src/host/Linux-SGX/sgx_api.h index 502c7f0bbc..6bf43ed603 100644 --- a/Pal/src/host/Linux-SGX/sgx_api.h +++ b/Pal/src/host/Linux-SGX/sgx_api.h @@ -41,8 +41,13 @@ int sgx_get_report (sgx_arch_hash_t * mrenclave, int sgx_verify_report (sgx_arch_report_t * report); +int sgx_accept(sgx_arch_secinfo_t* si, const void * addr); + +int sgx_modpe(sgx_arch_secinfo_t* si, const void * addr); + uint32_t rdrand (void); uint64_t rdfsbase (void); void wrfsbase (uint64_t addr); +int sgx_accept_pages(uint64_t sfl, size_t lo, size_t hi, bool executable); #endif /* SGX_API_H */ diff --git a/Pal/src/host/Linux-SGX/sgx_arch.h b/Pal/src/host/Linux-SGX/sgx_arch.h index c6a8f23719..ab739ad48f 100644 --- a/Pal/src/host/Linux-SGX/sgx_arch.h +++ b/Pal/src/host/Linux-SGX/sgx_arch.h @@ -166,6 +166,11 @@ typedef struct { #define SGX_SECINFO_FLAGS_TCS 0x100 #define SGX_SECINFO_FLAGS_REG 0x200 +/* EDMM PAGE STATUS */ +#define SGX_SECINFO_FLAGS_PENDING 0x08 +#define SGX_SECINFO_FLAGS_MODIFIED 0x010 +#define SGX_SECINFO_FLAGS_PR 0x020 + typedef struct { /* header part (signed) */ uint32_t header[4], vendor; @@ -264,6 +269,9 @@ typedef uint8_t sgx_arch_key128_t[16] __attribute__((aligned(16))); #define EREPORT 0 #define EGETKEY 1 #define EEXIT 4 +#define EACCEPT 5 +#define EMODPE 6 + #define LAUNCH_KEY 0 #define PROVISION_KEY 1 diff --git a/Pal/src/host/Linux-SGX/sgx_enclave.c b/Pal/src/host/Linux-SGX/sgx_enclave.c index 98baa20a75..c8c2fc4302 100644 --- a/Pal/src/host/Linux-SGX/sgx_enclave.c +++ b/Pal/src/host/Linux-SGX/sgx_enclave.c @@ -720,6 +720,25 @@ int ecall_thread_start (void) return sgx_ecall(ECALL_THREAD_START, NULL); } +int ecall_stack_expand(void * addr) +{ + EDEBUG(ECALL_STACK_EXPAND, addr); + return sgx_ecall(ECALL_STACK_EXPAND, addr); +} + +int ecall_thread_setup (void * thread_info) +{ + EDEBUG(ECALL_THREAD_SETUP, thread_info); + return sgx_ecall(ECALL_THREAD_SETUP, thread_info); +} + +int ecall_thread_create(void * thread_info) +{ + EDEBUG(ECALL_THREAD_CREATE, thread_info); + return sgx_ecall(ECALL_THREAD_CREATE, thread_info); +} + + void __abort(void) { INLINE_SYSCALL(exit_group, 1, -1); } diff --git a/Pal/src/host/Linux-SGX/sgx_enclave.h b/Pal/src/host/Linux-SGX/sgx_enclave.h index 98b54bba43..cd90cb5ca8 100644 --- a/Pal/src/host/Linux-SGX/sgx_enclave.h +++ b/Pal/src/host/Linux-SGX/sgx_enclave.h @@ -7,3 +7,9 @@ int ecall_enclave_start (const char ** arguments, const char ** environments); int ecall_thread_start (void); + +int ecall_stack_expand (void * fault_addr); + +int ecall_thread_setup (void * thread_info); + +int ecall_thread_create(void * thread_info); diff --git a/Pal/src/host/Linux-SGX/sgx_entry.S b/Pal/src/host/Linux-SGX/sgx_entry.S index ac4f6a92bd..96e89fd434 100644 --- a/Pal/src/host/Linux-SGX/sgx_entry.S +++ b/Pal/src/host/Linux-SGX/sgx_entry.S @@ -7,6 +7,14 @@ .type sgx_ecall, @function sgx_ecall: + push %rbp + mov %rsp,%rbp + + # Save registers + push %rbx + push %rcx + push %rdx + # put entry address in RDX lea sgx_entry(%rip), %rdx @@ -23,6 +31,14 @@ sgx_ecall: mov $EENTER, %rax ENCLU + # Restore Registers + pop %rdx + pop %rcx + pop %rbx + + pop %rbp + retq + .global async_exit_pointer .type async_exit_pointer, @function diff --git a/Pal/src/host/Linux-SGX/sgx_exception.c b/Pal/src/host/Linux-SGX/sgx_exception.c index 6fa4feaecd..80f89c21bf 100644 --- a/Pal/src/host/Linux-SGX/sgx_exception.c +++ b/Pal/src/host/Linux-SGX/sgx_exception.c @@ -226,7 +226,8 @@ static void _DkResumeSighandler (int signum, siginfo_t * info, struct ucontext * uc) { unsigned long rip = uc->uc_mcontext.gregs[REG_RIP]; - + unsigned long rax = uc->uc_mcontext.gregs[REG_RAX]; + #if SGX_HAS_FSGSBASE == 0 if (rip != (unsigned long) async_exit_pointer && rip != (unsigned long) double_async_exit) { @@ -264,7 +265,17 @@ static void _DkResumeSighandler (int signum, siginfo_t * info, break; } #if SGX_HAS_FSGSBASE != 0 - sgx_raise(event); + unsigned long fault_addr = (unsigned long)(info->si_addr); + unsigned long stack_start_addr = current_enclave->stackinfo.start_addr; + unsigned long stack_end_addr = current_enclave->stackinfo.end_addr; + + /* need to grow stack if it's in stack area with SIGBUS under EDMM */ + if (current_enclave->pal_sec.edmm_mode && (signum == SIGBUS && rax == ERESUME) + && (fault_addr <= stack_start_addr && fault_addr >= stack_end_addr)){ + ecall_stack_expand((void *)fault_addr); + } + else + sgx_raise(event); #else uc->uc_mcontext.gregs[REG_R9] = event; #endif diff --git a/Pal/src/host/Linux-SGX/sgx_framework.c b/Pal/src/host/Linux-SGX/sgx_framework.c index c4ea9f0e92..98d96195b6 100644 --- a/Pal/src/host/Linux-SGX/sgx_framework.c +++ b/Pal/src/host/Linux-SGX/sgx_framework.c @@ -297,6 +297,27 @@ int add_pages_to_enclave(sgx_arch_secs_t * secs, return 0; } +/* mktcs sends IOCTL to ask sgx driver to change + * the type of a regular page to TCS type */ +void mktcs(unsigned long tcs_addr) +{ +#if SDK_DRIVER_VERSION == KERNEL_VERSION(2, 0, 0) + struct sgx_range params; + memset(¶ms, 0 ,sizeof(struct sgx_range)); + params.start_addr = tcs_addr; + params.nr_pages = 1; + int ret = 0; + + ret = INLINE_SYSCALL(ioctl, 3, isgx_device, SGX_IOC_ENCLAVE_MKTCS, ¶ms); + if (IS_ERR(ret)) { + SGX_DBG(DBG_I, "Enclave MKTCS returned %d\n", ret); + return ; + } +#else + SGX_DBG(DBG_E, "EDMM is not supported by SDK before 2.0\n"); +#endif +} + int init_enclave(sgx_arch_secs_t * secs, sgx_arch_sigstruct_t * sigstruct, sgx_arch_token_t * token) diff --git a/Pal/src/host/Linux-SGX/sgx_internal.h b/Pal/src/host/Linux-SGX/sgx_internal.h index 1319c10b97..f5d9a5d735 100644 --- a/Pal/src/host/Linux-SGX/sgx_internal.h +++ b/Pal/src/host/Linux-SGX/sgx_internal.h @@ -62,6 +62,11 @@ uint16_t htons (uint16_t shortval); uint32_t ntohl (uint32_t longval); uint16_t ntohs (uint16_t shortval); +struct pal_enclave_stack_info { + unsigned long start_addr; + unsigned long end_addr; +}; + struct pal_enclave { /* attributes */ unsigned long baseaddr; @@ -69,6 +74,8 @@ struct pal_enclave { unsigned long thread_num; unsigned long ssaframesize; + /* stack information */ + struct pal_enclave_stack_info stackinfo; /* files */ int manifest; int exec; @@ -100,6 +107,7 @@ int add_pages_to_enclave(sgx_arch_secs_t * secs, enum sgx_page_type type, int prot, bool skip_eextend, const char * comment); +void mktcs(unsigned long tcs_addr); int init_enclave(sgx_arch_secs_t * secs, sgx_arch_sigstruct_t * sigstruct, @@ -119,7 +127,9 @@ void double_async_exit (void); int interrupt_thread (void * tcs); int clone_thread (void); -void create_tcs_mapper (void * tcs_base, unsigned int thread_num); +void create_tcs_mapper (unsigned long ssa_base, unsigned long tcs_base, unsigned long tls_base, unsigned long aux_stack_base, unsigned long enclave_entry, + unsigned int thread_num, unsigned int max_thread_num); + void map_tcs (unsigned int tid); void unmap_tcs (void); diff --git a/Pal/src/host/Linux-SGX/sgx_main.c b/Pal/src/host/Linux-SGX/sgx_main.c index eeefee378a..d14139519a 100644 --- a/Pal/src/host/Linux-SGX/sgx_main.c +++ b/Pal/src/host/Linux-SGX/sgx_main.c @@ -288,6 +288,17 @@ int initialize_enclave (struct pal_enclave * enclave) goto err; } + /* Reading sgx.edmm_mode from manifest */ + if (get_config(enclave->config, "sgx.edmm_mode", cfgbuf, CONFIG_MAX) <= 0) { + SGX_DBG(DBG_E, "edmm_mode is not specified, edmm mode is disabled by default\n"); + enclave->pal_sec.edmm_mode = 0; + } + + enclave->pal_sec.edmm_mode = parse_int(cfgbuf); + + if (enclave->pal_sec.edmm_mode) + enclave->size = parse_int("4G"); + /* Reading sgx.static_address from manifest */ if (get_config(enclave->config, "sgx.static_address", cfgbuf, CONFIG_MAX) > 0 && cfgbuf[0] == '1') @@ -322,7 +333,7 @@ int initialize_enclave (struct pal_enclave * enclave) }; struct mem_area * areas = - __alloca(sizeof(areas[0]) * (10 + enclave->thread_num)); + __alloca(sizeof(areas[0]) * (16 + MAX_THREAD_NUM)); int area_num = 0; #define set_area(_desc, _skip_eextend, _is_binary, _fd, _addr, _size, _prot, _type)\ @@ -338,25 +349,36 @@ int initialize_enclave (struct pal_enclave * enclave) set_area("manifest", false, false, enclave->manifest, 0, ALLOC_ALIGNUP(manifest_size), PROT_READ, SGX_PAGE_REG); + + /* leave the virtual address space for maximum supported thread number under EDMM */ + int allocated_thread_num = enclave->pal_sec.edmm_mode ? MAX_THREAD_NUM : enclave->thread_num; + struct mem_area * ssa_area = set_area("ssa", true, false, -1, 0, - enclave->thread_num * enclave->ssaframesize * SSAFRAMENUM, + allocated_thread_num * enclave->ssaframesize * SSAFRAMENUM, PROT_READ|PROT_WRITE, SGX_PAGE_REG); /* XXX: TCS should be part of measurement */ struct mem_area * tcs_area = - set_area("tcs", true, false, -1, 0, enclave->thread_num * pagesize, + set_area("tcs", true, false, -1, 0, allocated_thread_num * pagesize, 0, SGX_PAGE_TCS); /* XXX: TLS should be part of measurement */ struct mem_area * tls_area = - set_area("tls", true, false, -1, 0, enclave->thread_num * pagesize, + set_area("tls", true, false, -1, 0, allocated_thread_num * pagesize, PROT_READ|PROT_WRITE, SGX_PAGE_REG); /* XXX: the enclave stack should be part of measurement */ struct mem_area * stack_areas = &areas[area_num]; - for (int t = 0 ; t < enclave->thread_num ; t++) + for (int t = 0 ; t < allocated_thread_num ; t++) set_area("stack", true, false, -1, 0, ENCLAVE_STACK_SIZE, PROT_READ|PROT_WRITE, SGX_PAGE_REG); + /* XXX: EDMM: allocated two pages of auxiliary stack for dynamic stack grow + * and thread creation */ + struct mem_area * aux_stack_area = NULL; + if (enclave->pal_sec.edmm_mode) + aux_stack_area = set_area("aux_stack", true, false, -1, 0, AUX_STACK_SIZE, + PROT_READ|PROT_WRITE, SGX_PAGE_REG); + struct mem_area * pal_area = set_area("pal", false, true, enclave_image, 0, 0, 0, SGX_PAGE_REG); TRY(scan_enclave_binary, @@ -405,6 +427,10 @@ int initialize_enclave (struct pal_enclave * enclave) PROT_READ|PROT_WRITE|PROT_EXEC, SGX_PAGE_REG); } + /* store stack range info for dynamic stack grow */ + enclave->stackinfo.start_addr = stack_areas[0].addr + ENCLAVE_STACK_SIZE * PRESET_PAGESIZE; + enclave->stackinfo.end_addr = stack_areas[enclave->thread_num - 1].addr; + for (int i = 0 ; i < area_num ; i++) { if (areas[i].fd != -1 && areas[i].is_binary) { TRY(load_enclave_binary, @@ -419,7 +445,8 @@ int initialize_enclave (struct pal_enclave * enclave) PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); - for (int t = 0 ; t < enclave->thread_num ; t++) { + int tls_thread_num = enclave->pal_sec.edmm_mode ? enclave->thread_num + 1: enclave->thread_num; + for (int t = 0 ; t < tls_thread_num ; t++) { struct enclave_tls * gs = data + pagesize * t; gs->enclave_size = enclave->size; gs->tcs_offset = tcs_area->addr + pagesize * t; @@ -430,6 +457,16 @@ int initialize_enclave (struct pal_enclave * enclave) enclave_secs.baseaddr; gs->gpr = gs->ssa + enclave->ssaframesize - sizeof(sgx_arch_gpr_t); + + gs->ocall_pending = 0; + + if (enclave->pal_sec.edmm_mode){ + /* Each thread has its own region of aux stack from the whole aux_stack_area */ + unsigned long aux_stack_init_addr = aux_stack_area->addr + AUX_STACK_SIZE; + gs->aux_stack_offset = aux_stack_init_addr - t * AUX_STACK_SIZE_PER_THREAD; + gs->stack_commit_top = gs->initial_stack_offset; + } + } goto add_pages; @@ -439,8 +476,8 @@ int initialize_enclave (struct pal_enclave * enclave) data = (void *) INLINE_SYSCALL(mmap, 6, NULL, areas[i].size, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); - - for (int t = 0 ; t < enclave->thread_num ; t++) { + int tcs_thread_num = enclave->pal_sec.edmm_mode ? enclave->thread_num + 1 : enclave->thread_num; + for (int t = 0 ; t < tcs_thread_num ; t++) { sgx_arch_tcs_t * tcs = data + pagesize * t; memset(tcs, 0, pagesize); tcs->ossa = ssa_area->addr + @@ -465,19 +502,44 @@ int initialize_enclave (struct pal_enclave * enclave) areas[i].fd, 0); add_pages: - TRY(add_pages_to_enclave, - &enclave_secs, (void *) areas[i].addr, data, areas[i].size, - areas[i].type, areas[i].prot, areas[i].skip_eextend, - areas[i].desc); - + if (enclave->pal_sec.edmm_mode) { + + if(!strcmp_static(areas[i].desc, "free") && + !strcmp_static(areas[i].desc, "stack")) { + + /* EDMM: SSA/TLS/TCS only commit EPC pages for static threads + commit one more thread for dynamically create new thread context */ + unsigned long area_size = 0; + if (strcmp_static(areas[i].desc, "ssa")) + area_size = (enclave->thread_num + 1) * enclave->ssaframesize * SSAFRAMENUM; + else if (strcmp_static(areas[i].desc, "tcs") || strcmp_static(areas[i].desc, "tls")) + area_size = (enclave->thread_num + 1) * pagesize; + else + area_size = areas[i].size; + + TRY(add_pages_to_enclave, + &enclave_secs, (void *) areas[i].addr, data, area_size, + areas[i].type, areas[i].prot, areas[i].skip_eextend, + areas[i].desc); + } + } + else + TRY(add_pages_to_enclave, + &enclave_secs, (void *) areas[i].addr, data, areas[i].size, + areas[i].type, areas[i].prot, areas[i].skip_eextend, + areas[i].desc); if (data) INLINE_SYSCALL(munmap, 2, data, areas[i].size); } TRY(init_enclave, &enclave_secs, &enclave_sigstruct, &enclave_token); + + create_tcs_mapper(ssa_area->addr, enclave_secs.baseaddr + tcs_area->addr, + enclave_secs.baseaddr + tls_area->addr, + enclave->pal_sec.edmm_mode ? aux_stack_area->addr + AUX_STACK_SIZE:0, + enclave_entry_addr, + enclave->thread_num, enclave->pal_sec.edmm_mode? MAX_THREAD_NUM : enclave->thread_num); - create_tcs_mapper((void *) enclave_secs.baseaddr + tcs_area->addr, - enclave->thread_num); struct pal_sec * pal_sec = &enclave->pal_sec; diff --git a/Pal/src/host/Linux-SGX/sgx_thread.c b/Pal/src/host/Linux-SGX/sgx_thread.c index e17ae99b14..27f77773e0 100644 --- a/Pal/src/host/Linux-SGX/sgx_thread.c +++ b/Pal/src/host/Linux-SGX/sgx_thread.c @@ -16,36 +16,101 @@ __thread struct pal_enclave * current_enclave; __thread sgx_arch_tcs_t * current_tcs; +enum { + TCS_ALLOC = 0, + TCS_UNALLOC, +}; + struct thread_map { - unsigned int tid; - sgx_arch_tcs_t * tcs; + unsigned int tid; + unsigned int thread_index; + unsigned int status; + sgx_arch_tcs_t * tcs; + unsigned long tcs_addr; + unsigned long ssa_addr; + unsigned long tls_addr; + unsigned long aux_stack_addr; /* only applicable to EDMM */ + unsigned long enclave_entry; }; static sgx_arch_tcs_t * enclave_tcs; static int enclave_thread_num; +static int enclave_max_thread_num; static struct thread_map * enclave_thread_map; -void create_tcs_mapper (void * tcs_base, unsigned int thread_num) +/* create_tcs_mapper initializes the thread information for each threads + * thread_num: the number of threads statically allocated + * max_thread_num: the maximum number of threads could be allocated under EDMM + */ +void create_tcs_mapper (unsigned long ssa_base, unsigned long tcs_base, unsigned long tls_base, unsigned long aux_stack_base, unsigned long enclave_entry, + unsigned int thread_num, unsigned int max_thread_num) { - enclave_tcs = tcs_base; - enclave_thread_map = malloc(sizeof(struct thread_map) * thread_num); + enclave_tcs = (sgx_arch_tcs_t*)tcs_base; enclave_thread_num = thread_num; + enclave_max_thread_num = max_thread_num; - for (int i = 0 ; i < thread_num ; i++) { + enclave_thread_map = malloc(sizeof(struct thread_map) * enclave_max_thread_num); + + for (int i = 0 ; i < enclave_max_thread_num ; i++) { enclave_thread_map[i].tid = 0; + enclave_thread_map[i].thread_index = i; + enclave_thread_map[i].tcs = NULL; + enclave_thread_map[i].ssa_addr = ssa_base + i * pagesize * 2; + enclave_thread_map[i].tcs_addr = tcs_base + i * pagesize; + enclave_thread_map[i].tls_addr = tls_base + i * pagesize; + enclave_thread_map[i].aux_stack_addr = aux_stack_base ? aux_stack_base - i * AUX_STACK_SIZE_PER_THREAD: 0; + enclave_thread_map[i].enclave_entry = enclave_entry; enclave_thread_map[i].tcs = &enclave_tcs[i]; + + enclave_thread_map[i].status = TCS_UNALLOC; } } +void create_thread_context(struct thread_map * thread_info) +{ + /* using management thread for setup newly-created thread context */ + current_tcs = enclave_thread_map[enclave_thread_num].tcs; + + ecall_thread_setup((void*)thread_info); + + mktcs(thread_info->tcs_addr); + + ecall_thread_create((void*)thread_info); +} + void map_tcs (unsigned int tid) { - for (int i = 0 ; i < enclave_thread_num ; i++) + for (int i = 0 ; i < enclave_thread_num ; i++){ if (!enclave_thread_map[i].tid) { enclave_thread_map[i].tid = tid; current_tcs = enclave_thread_map[i].tcs; ((struct enclave_dbginfo *) DBGINFO_ADDR)->thread_tids[i] = tid; - break; + return ; + } + } + + /* EDMM create thread dynamically after static threads run out + * There is one thread at enclave_thead_map[enclave_thread_num] + * which is dedicated as management thread for creating new threads + * start to create threads with enclave_thread_map[enclave_thread_num + 1] + */ + for (int i = enclave_thread_num + 1; i < enclave_max_thread_num; i++){ + if (!enclave_thread_map[i].tid){ + printf("enclave_thread_map[%d].tcs_addr: %p\n", i, enclave_thread_map[i].tcs_addr); + + /* Allocate the thread context (SSA/TLS/TCS) for new + * thread if not allocated previously */ + if (enclave_thread_map[i].status == TCS_UNALLOC) { + // TODO: Potential race in map_tcs? need a mutex here? + create_thread_context(enclave_thread_map + i); + enclave_thread_map[i].status = TCS_ALLOC; + } + enclave_thread_map[i].tid = tid; + current_tcs = enclave_thread_map[i].tcs; + ((struct enclave_dbginfo *) DBGINFO_ADDR)->thread_tids[i] = tid; + return ; } + } } void unmap_tcs (void) diff --git a/Pal/src/host/Linux-SGX/sgx_tls.h b/Pal/src/host/Linux-SGX/sgx_tls.h index 5f49a71a4c..3c075c8966 100644 --- a/Pal/src/host/Linux-SGX/sgx_tls.h +++ b/Pal/src/host/Linux-SGX/sgx_tls.h @@ -19,6 +19,10 @@ struct enclave_tls { void * ustack_top; void * ustack; void * thread; + uint64_t aux_stack_offset; + uint64_t stack_commit_top; + uint64_t ecall_ret_target; + uint64_t ocall_pending; }; #ifndef DEBUG @@ -56,7 +60,9 @@ extern uint64_t dummy_debug_variable; #define SGX_USTACK_TOP 0x48 #define SGX_USTACK 0x50 #define SGX_THREAD 0x58 - +#define SGX_AUX_STACK_OFFSET 0x60 +#define SGX_ECALL_RET_TARGET 0x70 +#define SGX_OCALL_PENDING 0x78 #endif #endif /* __SGX_TLS_H__ */ diff --git a/Pal/src/host/Linux-SGX/signer/pal-sgx-sign b/Pal/src/host/Linux-SGX/signer/pal-sgx-sign index c5301d054e..428048f1a1 100755 --- a/Pal/src/host/Linux-SGX/signer/pal-sgx-sign +++ b/Pal/src/host/Linux-SGX/signer/pal-sgx-sign @@ -26,6 +26,8 @@ DEFAULT_ENCLAVE_SIZE = '256M' DEFAULT_THREAD_NUM = 4 ENCLAVE_HEAP_MIN = 0x10000 +MAX_THREAD_NUM = 16 +AUX_STACK_SIZE = PAGESIZE * 2 """ Utilities """ def roundup(addr): @@ -176,6 +178,10 @@ def get_enclave_attributes(manifest): if manifest_options[opt] in attributes: attributes.pop(manifest_options[opt]) + if 'sgx.disable_avx' in manifest: + if manifest['sgx.disable_avx'] == '1': + attributes.remove('XFRM_AVX') + flags_raw = struct.pack(" ENCLAVE_HEAP_MIN: free_areas.append(MemoryArea('free', addr=ENCLAVE_HEAP_MIN, size=populating - ENCLAVE_HEAP_MIN, - flags=PAGEINFO_R|PAGEINFO_W|PAGEINFO_X|PAGEINFO_REG)) + eadd_size=populating - ENCLAVE_HEAP_MIN, flags=PAGEINFO_R|PAGEINFO_W|PAGEINFO_X|PAGEINFO_REG)) return areas + free_areas @@ -474,6 +497,9 @@ def generate_measurement(attr, areas): raise Exception("wrong calculation") digest.update(start_zero + data + end_zero) + # Getting EDMM MODE + edmm_mode = attr['edmm_mode'] + for area in areas: if area.file: with open(area.file, 'rb') as f: @@ -505,8 +531,13 @@ def generate_measurement(attr, areas): os.stat(area.file).st_size, area.size, area.desc, area.flags) else: - for a in range(area.addr, area.addr + area.size, PAGESIZE): + if edmm_mode != 1: + for a in range(area.addr, area.addr + area.size, PAGESIZE): do_eadd(mrenclave, a, area.flags) + else: + for a in range(area.addr, area.addr + area.eadd_size, PAGESIZE): + if area.desc != "free" and area.desc != "stack": + do_eadd(mrenclave, a, area.flags) print_area(area.addr, area.size, area.flags, area.desc, False) return mrenclave.finalize() @@ -699,6 +730,7 @@ if __name__ == "__main__": ('thread_num', str(DEFAULT_THREAD_NUM), parse_int), ('isvprodid', '0', parse_int), ('isvsvn', '0', parse_int), + ('edmm_mode', '0', parse_int), ]: if 'sgx.' + key not in manifest: manifest['sgx.' + key] = default @@ -706,6 +738,10 @@ if __name__ == "__main__": (attr['flags'], attr['xfrms'], attr['miscs']) = get_enclave_attributes(manifest) + # Set Enclave size to 4G when EDMM enabled + if (attr['edmm_mode'] == 1) : + attr['enclave_size'] = parse_size('4G') + print >>sys.stderr, "Attributes:" print >>sys.stderr, " size: %d" % (attr['enclave_size']) print >>sys.stderr, " threadnum: %d" % (attr['thread_num']) @@ -714,8 +750,9 @@ if __name__ == "__main__": print >>sys.stderr, " flags: %016x" % (bytes_to_int(attr['flags'])) print >>sys.stderr, " xfrms: %016x" % (bytes_to_int(attr['xfrms'])) print >>sys.stderr, " miscs: %08x" % (bytes_to_int(attr['miscs'])) + print >>sys.stderr, " edmm_mode: %d" % (attr['edmm_mode']) - # Get trusted checksums and measurements + # Get trusted checksums and measurements print >>sys.stderr, "Trusted files:" for key, val in get_trusted_files(manifest, args).items(): (uri, target, checksum) = val