mirror of
https://github.com/clearlinux/graphene.git
synced 2026-09-03 20:31:40 +00:00
[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().
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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. */
|
||||
|
||||
Reference in New Issue
Block a user