From 95cd78c3c8d635bd5ae76f082177d60bd798cfbd Mon Sep 17 00:00:00 2001 From: Dmitrii Kuvaiskii Date: Tue, 24 Nov 2020 05:56:43 -0800 Subject: [PATCH] [Pal/Linux-SGX] Allow ocall_mmap_untrusted() with fixed address This functionality is currently not used by Linux-SGX PAL but will be required in the IOCTL emulation in future commits. Also, function signatures of ocall_mmap_untrusted() and ocall_munmap_untrusted() now resemble the signatures of mmap() and munmap(). --- Pal/src/host/Linux-SGX/db_files.c | 8 +- Pal/src/host/Linux-SGX/db_rtld.c | 8 +- Pal/src/host/Linux-SGX/enclave_framework.c | 2 +- Pal/src/host/Linux-SGX/enclave_ocalls.c | 92 ++++++++++++++++------ Pal/src/host/Linux-SGX/enclave_ocalls.h | 4 +- Pal/src/host/Linux-SGX/enclave_untrusted.c | 6 +- Pal/src/host/Linux-SGX/ocall_types.h | 11 +-- Pal/src/host/Linux-SGX/sgx_enclave.c | 12 +-- Pal/src/host/Linux-SGX/sgx_tls.h | 2 +- 9 files changed, 92 insertions(+), 53 deletions(-) diff --git a/Pal/src/host/Linux-SGX/db_files.c b/Pal/src/host/Linux-SGX/db_files.c index 2d884cdd..bde33248 100644 --- a/Pal/src/host/Linux-SGX/db_files.c +++ b/Pal/src/host/Linux-SGX/db_files.c @@ -412,7 +412,6 @@ static int file_map(PAL_HANDLE handle, void** addr, int prot, uint64_t offset, u sgx_stub_t* stubs = (sgx_stub_t*)handle->file.stubs; uint64_t total = handle->file.total; void* mem = *addr; - void* umem; int ret; /* @@ -421,7 +420,8 @@ static int file_map(PAL_HANDLE handle, void** addr, int prot, uint64_t offset, u * does not request a specific address. */ if (!mem && !stubs && !(prot & PAL_PROT_WRITECOPY)) { - ret = ocall_mmap_untrusted(handle->file.fd, offset, size, PAL_PROT_TO_LINUX(prot), &mem); + ret = ocall_mmap_untrusted(&mem, size, PAL_PROT_TO_LINUX(prot), MAP_SHARED, handle->file.fd, + offset); if (!IS_ERR(ret)) *addr = mem; return IS_ERR(ret) ? unix_to_pal_error(ERRNO(ret)) : ret; @@ -451,7 +451,9 @@ static int file_map(PAL_HANDLE handle, void** addr, int prot, uint64_t offset, u map_end = ALLOC_ALIGN_UP(end); } - ret = ocall_mmap_untrusted(handle->file.fd, map_start, map_end - map_start, PROT_READ, &umem); + void* umem = NULL; + ret = ocall_mmap_untrusted(&umem, map_end - map_start, PROT_READ, MAP_SHARED, handle->file.fd, + map_start); if (IS_ERR(ret)) { SGX_DBG(DBG_E, "file_map - ocall returned %d\n", ret); return unix_to_pal_error(ERRNO(ret)); diff --git a/Pal/src/host/Linux-SGX/db_rtld.c b/Pal/src/host/Linux-SGX/db_rtld.c index b45e6363..8563c509 100644 --- a/Pal/src/host/Linux-SGX/db_rtld.c +++ b/Pal/src/host/Linux-SGX/db_rtld.c @@ -145,8 +145,8 @@ void _DkDebugAddMap(struct link_map* map) { shdr = __alloca(shdrsz); unsigned long s = ALLOC_ALIGN_DOWN(ehdr->e_shoff); unsigned long e = ALLOC_ALIGN_UP(ehdr->e_shoff + shdrsz); - void* umem; - ocall_mmap_untrusted(fd, s, e - s, PROT_READ, &umem); + void* umem = NULL; + ocall_mmap_untrusted(&umem, e - s, PROT_READ, MAP_SHARED, fd, s); memcpy(shdr, umem + ehdr->e_shoff - s, shdrsz); ocall_munmap_untrusted(umem, e - s); } @@ -167,8 +167,8 @@ void _DkDebugAddMap(struct link_map* map) { shstrtab = __alloca(shstrsz); unsigned long s = ALLOC_ALIGN_DOWN(shstroff); unsigned long e = ALLOC_ALIGN_UP(shstroff + shstrsz); - void* umem; - ocall_mmap_untrusted(fd, s, e - s, PROT_READ, &umem); + void* umem = NULL; + ocall_mmap_untrusted(&umem, e - s, PROT_READ, MAP_SHARED, fd, s); memcpy((void*)shstrtab, umem + shstroff - s, shstrsz); ocall_munmap_untrusted(umem, e - s); } diff --git a/Pal/src/host/Linux-SGX/enclave_framework.c b/Pal/src/host/Linux-SGX/enclave_framework.c index 31500034..e2227ad6 100644 --- a/Pal/src/host/Linux-SGX/enclave_framework.c +++ b/Pal/src/host/Linux-SGX/enclave_framework.c @@ -381,7 +381,7 @@ int load_trusted_file(PAL_HANDLE file, sgx_stub_t** stubptr, uint64_t* sizeptr, * caller's responsibility to unmap those areas after use */ *sizeptr = tf->size; if (*sizeptr) { - ret = ocall_mmap_untrusted(fd, 0, tf->size, PROT_READ, umem); + ret = ocall_mmap_untrusted(umem, tf->size, PROT_READ, MAP_SHARED, fd, /*offset=*/0); if (IS_ERR(ret)) { *umem = NULL; ret = unix_to_pal_error(ERRNO(ret)); diff --git a/Pal/src/host/Linux-SGX/enclave_ocalls.c b/Pal/src/host/Linux-SGX/enclave_ocalls.c index c2521945..58fd5e1a 100644 --- a/Pal/src/host/Linux-SGX/enclave_ocalls.c +++ b/Pal/src/host/Linux-SGX/enclave_ocalls.c @@ -158,7 +158,7 @@ noreturn void ocall_exit(int exitcode, int is_exitgroup) { } } -int ocall_mmap_untrusted(int fd, uint64_t offset, size_t size, unsigned short prot, void** mem) { +int ocall_mmap_untrusted(void** addrptr, size_t size, int prot, int flags, int fd, off_t offset) { int retval = 0; ms_ocall_mmap_untrusted_t* ms; @@ -169,49 +169,82 @@ int ocall_mmap_untrusted(int fd, uint64_t offset, size_t size, unsigned short pr return -EPERM; } - WRITE_ONCE(ms->ms_fd, fd); - WRITE_ONCE(ms->ms_offset, offset); + if (!addrptr) { + sgx_reset_ustack(old_ustack); + return -EINVAL; + } + + void* requested_addr = *addrptr; + + if (flags & MAP_FIXED) { + if (!sgx_is_completely_outside_enclave(requested_addr, size) || + !IS_ALLOC_ALIGNED_PTR(requested_addr)) { + sgx_reset_ustack(old_ustack); + return -EINVAL; + } + } else { + requested_addr = NULL; /* for sanity */ + } + + WRITE_ONCE(ms->ms_addr, requested_addr); WRITE_ONCE(ms->ms_size, size); WRITE_ONCE(ms->ms_prot, prot); + WRITE_ONCE(ms->ms_flags, flags); + WRITE_ONCE(ms->ms_fd, fd); + WRITE_ONCE(ms->ms_offset, offset); do { retval = sgx_exitless_ocall(OCALL_MMAP_UNTRUSTED, ms); } while (retval == -EINTR); - if (!retval) { - if (!sgx_copy_ptr_to_enclave(mem, READ_ONCE(ms->ms_mem), size)) { + if (IS_ERR(retval)) { + sgx_reset_ustack(old_ustack); + return IS_UNIX_ERR(retval) ? retval : -EPERM; + } + + void* returned_addr = READ_ONCE(ms->ms_addr); + if (flags & MAP_FIXED) { + /* addrptr already contains the mmap'ed address, no need to update it */ + if (returned_addr != requested_addr) { + sgx_reset_ustack(old_ustack); + return -EPERM; + } + } else { + /* update addrptr with the mmap'ed address */ + if (!sgx_copy_ptr_to_enclave(addrptr, returned_addr, size)) { sgx_reset_ustack(old_ustack); return -EPERM; } } sgx_reset_ustack(old_ustack); - return retval; + return 0; } -int ocall_munmap_untrusted(const void* mem, size_t size) { +int ocall_munmap_untrusted(const void* addr, size_t size) { int retval = 0; ms_ocall_munmap_untrusted_t* ms; - void* old_ustack = sgx_prepare_ustack(); - if (!sgx_is_completely_outside_enclave(mem, size)) { - sgx_reset_ustack(old_ustack); + if (!sgx_is_completely_outside_enclave(addr, size) || !IS_ALLOC_ALIGNED_PTR(addr)) return -EINVAL; - } + void* old_ustack = sgx_prepare_ustack(); ms = sgx_alloc_on_ustack_aligned(sizeof(*ms), alignof(*ms)); if (!ms) { sgx_reset_ustack(old_ustack); return -EPERM; } - WRITE_ONCE(ms->ms_mem, mem); + WRITE_ONCE(ms->ms_addr, addr); WRITE_ONCE(ms->ms_size, size); do { retval = sgx_exitless_ocall(OCALL_MUNMAP_UNTRUSTED, ms); } while (retval == -EINTR); + if (IS_ERR(retval) && !IS_UNIX_ERR(retval)) + retval = -EPERM; + sgx_reset_ustack(old_ustack); return retval; } @@ -228,16 +261,22 @@ int ocall_munmap_untrusted(const void* mem, size_t size) { * handling do not use the cache and always explicitly mmap/munmap untrusted memory; 'need_munmap' * indicates whether explicit munmap is needed at the end of such OCALL. */ -static int ocall_mmap_untrusted_cache(size_t size, void** mem, bool* need_munmap) { +static int ocall_mmap_untrusted_cache(size_t size, void** addrptr, bool* need_munmap) { + int ret; + + *addrptr = NULL; *need_munmap = false; + struct untrusted_area* cache = &get_tcb_trts()->untrusted_area_cache; + uint64_t in_use = 0; if (!__atomic_compare_exchange_n(&cache->in_use, &in_use, 1, /*weak=*/false, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) { /* AEX signal handling case: cache is in use, so make explicit mmap/munmap */ - int retval = ocall_mmap_untrusted(-1, 0, size, PROT_READ | PROT_WRITE, mem); - if (IS_ERR(retval)) { - return retval; + ret = ocall_mmap_untrusted(addrptr, size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, /*fd=*/-1, /*offset=*/0); + if (IS_ERR(ret)) { + return ret; } *need_munmap = true; return 0; @@ -246,32 +285,33 @@ static int ocall_mmap_untrusted_cache(size_t size, void** mem, bool* need_munmap /* normal execution case: cache was not in use, so use it/allocate new one for reuse */ if (cache->valid) { if (cache->size >= size) { - *mem = cache->mem; + *addrptr = cache->addr; return 0; } - int retval = ocall_munmap_untrusted(cache->mem, cache->size); - if (IS_ERR(retval)) { + ret = ocall_munmap_untrusted(cache->addr, cache->size); + if (IS_ERR(ret)) { cache->valid = false; __atomic_store_n(&cache->in_use, 0, __ATOMIC_RELAXED); - return retval; + return ret; } } - int retval = ocall_mmap_untrusted(-1, 0, size, PROT_READ | PROT_WRITE, mem); - if (IS_ERR(retval)) { + ret = ocall_mmap_untrusted(addrptr, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, + /*fd=*/-1, /*offset=*/0); + if (IS_ERR(ret)) { cache->valid = false; __atomic_store_n(&cache->in_use, 0, __ATOMIC_RELAXED); } else { cache->valid = true; - cache->mem = *mem; + cache->addr = *addrptr; cache->size = size; } - return retval; + return ret; } -static void ocall_munmap_untrusted_cache(void* mem, size_t size, bool need_munmap) { +static void ocall_munmap_untrusted_cache(void* addr, size_t size, bool need_munmap) { if (need_munmap) { - ocall_munmap_untrusted(mem, size); + ocall_munmap_untrusted(addr, size); /* there is not much we can do in case of error */ } else { struct untrusted_area* cache = &get_tcb_trts()->untrusted_area_cache; diff --git a/Pal/src/host/Linux-SGX/enclave_ocalls.h b/Pal/src/host/Linux-SGX/enclave_ocalls.h index ef8cebc7..e19581a9 100644 --- a/Pal/src/host/Linux-SGX/enclave_ocalls.h +++ b/Pal/src/host/Linux-SGX/enclave_ocalls.h @@ -14,9 +14,9 @@ noreturn void ocall_exit(int exitcode, int is_exitgroup); -int ocall_mmap_untrusted(int fd, uint64_t offset, uint64_t size, unsigned short prot, void** mem); +int ocall_mmap_untrusted(void** addrptr, size_t size, int prot, int flags, int fd, off_t offset); -int ocall_munmap_untrusted(const void* mem, uint64_t size); +int ocall_munmap_untrusted(const void* addr, size_t size); int ocall_cpuid(unsigned int leaf, unsigned int subleaf, unsigned int values[4]); diff --git a/Pal/src/host/Linux-SGX/enclave_untrusted.c b/Pal/src/host/Linux-SGX/enclave_untrusted.c index 70c95667..c173f1e9 100644 --- a/Pal/src/host/Linux-SGX/enclave_untrusted.c +++ b/Pal/src/host/Linux-SGX/enclave_untrusted.c @@ -19,9 +19,9 @@ static size_t g_page_size = PRESET_PAGESIZE; static inline void* __malloc(int size) { void* addr = NULL; - - ocall_mmap_untrusted(-1, 0, size, PROT_READ | PROT_WRITE, &addr); - return addr; + int ret = ocall_mmap_untrusted(&addr, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, + /*fd=*/-1, /*offset=*/0); + return IS_ERR(ret) ? NULL : addr; } #define system_malloc(size) __malloc(size) diff --git a/Pal/src/host/Linux-SGX/ocall_types.h b/Pal/src/host/Linux-SGX/ocall_types.h index 6e151aa9..ea64d1d0 100644 --- a/Pal/src/host/Linux-SGX/ocall_types.h +++ b/Pal/src/host/Linux-SGX/ocall_types.h @@ -73,15 +73,16 @@ typedef struct { } ms_ocall_exit_t; typedef struct { - int ms_fd; - uint64_t ms_offset; + void* ms_addr; size_t ms_size; - unsigned short ms_prot; - void* ms_mem; + int ms_prot; + int ms_flags; + int ms_fd; + off_t ms_offset; } ms_ocall_mmap_untrusted_t; typedef struct { - const void* ms_mem; + const void* ms_addr; size_t ms_size; } ms_ocall_munmap_untrusted_t; diff --git a/Pal/src/host/Linux-SGX/sgx_enclave.c b/Pal/src/host/Linux-SGX/sgx_enclave.c index 04a8e543..7f3436f0 100644 --- a/Pal/src/host/Linux-SGX/sgx_enclave.c +++ b/Pal/src/host/Linux-SGX/sgx_enclave.c @@ -96,25 +96,21 @@ static long sgx_ocall_exit(void* pms) { static long sgx_ocall_mmap_untrusted(void* pms) { ms_ocall_mmap_untrusted_t* ms = (ms_ocall_mmap_untrusted_t*)pms; void* addr; - ODEBUG(OCALL_MMAP_UNTRUSTED, ms); - addr = (void*)INLINE_SYSCALL(mmap, 6, NULL, ms->ms_size, - ms->ms_prot, - (ms->ms_fd == -1) ? MAP_ANONYMOUS | MAP_PRIVATE - : MAP_FILE | MAP_SHARED, + + addr = (void*)INLINE_SYSCALL(mmap, 6, ms->ms_addr, ms->ms_size, ms->ms_prot, ms->ms_flags, ms->ms_fd, ms->ms_offset); if (IS_ERR_P(addr)) return -ERRNO_P(addr); - ms->ms_mem = addr; + ms->ms_addr = addr; return 0; } static long sgx_ocall_munmap_untrusted(void* pms) { ms_ocall_munmap_untrusted_t* ms = (ms_ocall_munmap_untrusted_t*)pms; ODEBUG(OCALL_MUNMAP_UNTRUSTED, ms); - INLINE_SYSCALL(munmap, 2, ALLOC_ALIGN_DOWN_PTR(ms->ms_mem), - ALLOC_ALIGN_UP_PTR(ms->ms_mem + ms->ms_size) - ALLOC_ALIGN_DOWN_PTR(ms->ms_mem)); + INLINE_SYSCALL(munmap, 2, ms->ms_addr, ms->ms_size); return 0; } diff --git a/Pal/src/host/Linux-SGX/sgx_tls.h b/Pal/src/host/Linux-SGX/sgx_tls.h index 7c3c4f0e..732fbf40 100644 --- a/Pal/src/host/Linux-SGX/sgx_tls.h +++ b/Pal/src/host/Linux-SGX/sgx_tls.h @@ -6,7 +6,7 @@ #include "pal.h" struct untrusted_area { - void* mem; + void* addr; size_t size; uint64_t in_use; /* must be uint64_t, because SET_ENCLAVE_TLS() currently supports only 8-byte * types. TODO: fix this. */