mirror of
https://github.com/clearlinux/graphene.git
synced 2026-09-06 13:51:28 +00:00
[Pal/Linux-SGX] Cache untrusted memory region for read/write/recv/send OCALLs
This commit introduces per-enclave-thread `untrusted_area_cache` that memorizes the untrusted memory area to avoid mmap()/munmap() per each read/write/recv/send IO OCALL. This is a performance optimization to reduce the number of issued OCALLs.
This commit is contained in:
committed by
Dmitrii Kuvaiskii
parent
4570e70b6d
commit
be9e85625a
@@ -54,6 +54,7 @@ void handle_ecall (long ecall_index, void * ecall_args, void * exit_target,
|
||||
SET_ENCLAVE_TLS(ustack_top, untrusted_stack);
|
||||
SET_ENCLAVE_TLS(ustack, untrusted_stack);
|
||||
SET_ENCLAVE_TLS(clear_child_tid, NULL);
|
||||
SET_ENCLAVE_TLS(untrusted_area_cache.in_use, 0UL);
|
||||
|
||||
if (atomic_cmpxchg(&enclave_start_called, 0, 1) == 0) {
|
||||
// ENCLAVE_START not yet called, so only valid ecall is ENCLAVE_START.
|
||||
|
||||
@@ -94,6 +94,68 @@ int ocall_munmap_untrusted (const void * mem, uint64_t size)
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Memorize untrusted memory area to avoid mmap/munmap per each read/write IO. Because this cache
|
||||
* is per-thread, we don't worry about concurrency. The cache will be carried over thread
|
||||
* exit/creation. On fork/exec emulation, untrusted code does vfork/exec, so the mmapped cache
|
||||
* will be released by exec host syscall.
|
||||
*
|
||||
* In case of AEX and consequent signal handling, current thread may be interrupted in the middle
|
||||
* of using the cache. If there are OCALLs during signal handling, they could interfere with the
|
||||
* normal-execution use of the cache, so 'in_use' atomic protects against it. OCALLs during signal
|
||||
* 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(uint64_t size, void** mem, bool* need_munmap) {
|
||||
*need_munmap = false;
|
||||
struct untrusted_area* cache = &get_tcb_trts()->untrusted_area_cache;
|
||||
int in_use = 0;
|
||||
if (!__atomic_compare_exchange_n(&cache->in_use, &in_use, 1, 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;
|
||||
}
|
||||
*need_munmap = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
return 0;
|
||||
}
|
||||
int retval = ocall_munmap_untrusted(cache->mem, cache->size);
|
||||
if (IS_ERR(retval)) {
|
||||
cache->valid = false;
|
||||
__atomic_store_n(&cache->in_use, 0, __ATOMIC_RELAXED);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
int retval = ocall_mmap_untrusted(-1, 0, size, PROT_READ | PROT_WRITE, mem);
|
||||
if (IS_ERR(retval)) {
|
||||
cache->valid = false;
|
||||
__atomic_store_n(&cache->in_use, 0, __ATOMIC_RELAXED);
|
||||
} else {
|
||||
cache->valid = true;
|
||||
cache->mem = *mem;
|
||||
cache->size = size;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void ocall_munmap_untrusted_cache(void* mem, uint64_t size, bool need_munmap) {
|
||||
if (need_munmap) {
|
||||
ocall_munmap_untrusted(mem, size);
|
||||
/* there is not much we can do in case of error */
|
||||
} else {
|
||||
struct untrusted_area* cache = &get_tcb_trts()->untrusted_area_cache;
|
||||
__atomic_store_n(&cache->in_use, 0, __ATOMIC_RELAXED);
|
||||
}
|
||||
}
|
||||
|
||||
int ocall_cpuid (unsigned int leaf, unsigned int subleaf,
|
||||
unsigned int values[4])
|
||||
{
|
||||
@@ -173,9 +235,10 @@ int ocall_read(int fd, void* buf, unsigned int count) {
|
||||
void* obuf = NULL;
|
||||
ms_ocall_read_t* ms;
|
||||
void* ms_buf;
|
||||
bool need_munmap = false;
|
||||
|
||||
if (count > MAX_UNTRUSTED_STACK_BUF) {
|
||||
retval = ocall_mmap_untrusted(-1, 0, ALLOC_ALIGN_UP(count), PROT_READ | PROT_WRITE, &obuf);
|
||||
retval = ocall_mmap_untrusted_cache(ALLOC_ALIGN_UP(count), &obuf, &need_munmap);
|
||||
if (IS_ERR(retval))
|
||||
return retval;
|
||||
ms_buf = obuf;
|
||||
@@ -209,7 +272,7 @@ int ocall_read(int fd, void* buf, unsigned int count) {
|
||||
out:
|
||||
sgx_reset_ustack();
|
||||
if (obuf)
|
||||
ocall_munmap_untrusted(obuf, ALLOC_ALIGN_UP(count));
|
||||
ocall_munmap_untrusted_cache(obuf, ALLOC_ALIGN_UP(count), need_munmap);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -218,6 +281,7 @@ int ocall_write(int fd, const void* buf, unsigned int count) {
|
||||
void* obuf = NULL;
|
||||
ms_ocall_write_t* ms;
|
||||
const void* ms_buf;
|
||||
bool need_munmap = false;
|
||||
|
||||
if (sgx_is_completely_outside_enclave(buf, count)) {
|
||||
/* buf is in untrusted memory (e.g., allowed file mmaped in untrusted memory) */
|
||||
@@ -226,7 +290,7 @@ int ocall_write(int fd, const void* buf, unsigned int count) {
|
||||
/* typical case of buf inside of enclave memory */
|
||||
if (count > MAX_UNTRUSTED_STACK_BUF) {
|
||||
/* buf is too big and may overflow untrusted stack, so use untrusted heap */
|
||||
retval = ocall_mmap_untrusted(-1, 0, ALLOC_ALIGN_UP(count), PROT_READ | PROT_WRITE, &obuf);
|
||||
retval = ocall_mmap_untrusted_cache(ALLOC_ALIGN_UP(count), &obuf, &need_munmap);
|
||||
if (IS_ERR(retval))
|
||||
return retval;
|
||||
memcpy(obuf, buf, count);
|
||||
@@ -258,7 +322,7 @@ int ocall_write(int fd, const void* buf, unsigned int count) {
|
||||
out:
|
||||
sgx_reset_ustack();
|
||||
if (obuf)
|
||||
ocall_munmap_untrusted(obuf, ALLOC_ALIGN_UP(count));
|
||||
ocall_munmap_untrusted_cache(obuf, ALLOC_ALIGN_UP(count), need_munmap);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -267,9 +331,10 @@ ssize_t ocall_pread(int fd, void* buf, size_t count, off_t offset) {
|
||||
void* obuf = NULL;
|
||||
ms_ocall_pread_t* ms;
|
||||
void* ms_buf;
|
||||
bool need_munmap = false;
|
||||
|
||||
if (count > MAX_UNTRUSTED_STACK_BUF) {
|
||||
retval = ocall_mmap_untrusted(-1, 0, ALLOC_ALIGN_UP(count), PROT_READ | PROT_WRITE, &obuf);
|
||||
retval = ocall_mmap_untrusted_cache(ALLOC_ALIGN_UP(count), &obuf, &need_munmap);
|
||||
if (IS_ERR(retval))
|
||||
return retval;
|
||||
ms_buf = obuf;
|
||||
@@ -302,7 +367,7 @@ ssize_t ocall_pread(int fd, void* buf, size_t count, off_t offset) {
|
||||
out:
|
||||
sgx_reset_ustack();
|
||||
if (obuf)
|
||||
ocall_munmap_untrusted(obuf, ALLOC_ALIGN_UP(count));
|
||||
ocall_munmap_untrusted_cache(obuf, ALLOC_ALIGN_UP(count), need_munmap);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -311,6 +376,7 @@ ssize_t ocall_pwrite(int fd, const void* buf, size_t count, off_t offset) {
|
||||
void* obuf = NULL;
|
||||
ms_ocall_pwrite_t* ms;
|
||||
const void* ms_buf;
|
||||
bool need_munmap = false;
|
||||
|
||||
if (sgx_is_completely_outside_enclave(buf, count)) {
|
||||
/* buf is in untrusted memory (e.g., allowed file mmaped in untrusted memory) */
|
||||
@@ -319,7 +385,7 @@ ssize_t ocall_pwrite(int fd, const void* buf, size_t count, off_t offset) {
|
||||
/* typical case of buf inside of enclave memory */
|
||||
if (count > MAX_UNTRUSTED_STACK_BUF) {
|
||||
/* buf is too big and may overflow untrusted stack, so use untrusted heap */
|
||||
retval = ocall_mmap_untrusted(-1, 0, ALLOC_ALIGN_UP(count), PROT_READ | PROT_WRITE, &obuf);
|
||||
retval = ocall_mmap_untrusted_cache(ALLOC_ALIGN_UP(count), &obuf, &need_munmap);
|
||||
if (IS_ERR(retval))
|
||||
return retval;
|
||||
memcpy(obuf, buf, count);
|
||||
@@ -352,7 +418,7 @@ ssize_t ocall_pwrite(int fd, const void* buf, size_t count, off_t offset) {
|
||||
out:
|
||||
sgx_reset_ustack();
|
||||
if (obuf)
|
||||
ocall_munmap_untrusted(obuf, ALLOC_ALIGN_UP(count));
|
||||
ocall_munmap_untrusted_cache(obuf, ALLOC_ALIGN_UP(count), need_munmap);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -794,9 +860,10 @@ int ocall_recv (int sockfd, void * buf, unsigned int count,
|
||||
unsigned int addrlen = addrlenptr ? *addrlenptr : 0;
|
||||
uint64_t controllen = controllenptr ? *controllenptr : 0;
|
||||
ms_ocall_recv_t * ms;
|
||||
bool need_munmap = false;
|
||||
|
||||
if ((count + addrlen + controllen) > MAX_UNTRUSTED_STACK_BUF) {
|
||||
retval = ocall_mmap_untrusted(-1, 0, ALLOC_ALIGN_UP(count), PROT_READ | PROT_WRITE, &obuf);
|
||||
retval = ocall_mmap_untrusted_cache(ALLOC_ALIGN_UP(count), &obuf, &need_munmap);
|
||||
if (IS_ERR(retval))
|
||||
return retval;
|
||||
}
|
||||
@@ -853,7 +920,7 @@ int ocall_recv (int sockfd, void * buf, unsigned int count,
|
||||
out:
|
||||
sgx_reset_ustack();
|
||||
if (obuf)
|
||||
ocall_munmap_untrusted(obuf, ALLOC_ALIGN_UP(count));
|
||||
ocall_munmap_untrusted_cache(obuf, ALLOC_ALIGN_UP(count), need_munmap);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -864,6 +931,7 @@ int ocall_send (int sockfd, const void * buf, unsigned int count,
|
||||
int retval = 0;
|
||||
void * obuf = NULL;
|
||||
ms_ocall_send_t * ms;
|
||||
bool need_munmap;
|
||||
|
||||
if (sgx_is_completely_outside_enclave(buf, count)) {
|
||||
/* buf is in untrusted memory (e.g., allowed file mmaped in untrusted memory) */
|
||||
@@ -872,7 +940,7 @@ int ocall_send (int sockfd, const void * buf, unsigned int count,
|
||||
/* typical case of buf inside of enclave memory */
|
||||
if ((count + addrlen + controllen) > MAX_UNTRUSTED_STACK_BUF) {
|
||||
/* buf is too big and may overflow untrusted stack, so use untrusted heap */
|
||||
retval = ocall_mmap_untrusted(-1, 0, ALLOC_ALIGN_UP(count), PROT_READ | PROT_WRITE, &obuf);
|
||||
retval = ocall_mmap_untrusted_cache(ALLOC_ALIGN_UP(count), &obuf, &need_munmap);
|
||||
if (IS_ERR(retval))
|
||||
return retval;
|
||||
memcpy(obuf, buf, count);
|
||||
@@ -909,7 +977,7 @@ int ocall_send (int sockfd, const void * buf, unsigned int count,
|
||||
out:
|
||||
sgx_reset_ustack();
|
||||
if (obuf && obuf != buf)
|
||||
ocall_munmap_untrusted(obuf, ALLOC_ALIGN_UP(count));
|
||||
ocall_munmap_untrusted_cache(obuf, ALLOC_ALIGN_UP(count), need_munmap);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,13 @@
|
||||
|
||||
#include <pal.h>
|
||||
|
||||
struct untrusted_area {
|
||||
void* mem;
|
||||
uint64_t size;
|
||||
uint64_t in_use;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
/*
|
||||
* Beside the classic thread local storage (like ustack, thread, etc.) the TLS
|
||||
* area is also used to pass parameters needed during enclave or thread
|
||||
@@ -11,33 +18,33 @@
|
||||
*/
|
||||
struct enclave_tls {
|
||||
PAL_TCB common;
|
||||
struct {
|
||||
/* private to Linux-SGX PAL */
|
||||
uint64_t enclave_size;
|
||||
uint64_t tcs_offset;
|
||||
uint64_t initial_stack_offset;
|
||||
uint64_t tmp_rip;
|
||||
uint64_t sig_stack_low;
|
||||
uint64_t sig_stack_high;
|
||||
void* ecall_return_addr;
|
||||
void* ssa;
|
||||
sgx_pal_gpr_t* gpr;
|
||||
void* exit_target;
|
||||
void* fsbase;
|
||||
void* pre_ocall_stack;
|
||||
void* ustack_top;
|
||||
void* ustack;
|
||||
struct pal_handle_thread* thread;
|
||||
uint64_t ocall_exit_called;
|
||||
uint64_t thread_started;
|
||||
uint64_t ready_for_exceptions;
|
||||
uint64_t manifest_size;
|
||||
void* heap_min;
|
||||
void* heap_max;
|
||||
void* exec_addr;
|
||||
uint64_t exec_size;
|
||||
int* clear_child_tid;
|
||||
};
|
||||
|
||||
/* private to Linux-SGX PAL */
|
||||
uint64_t enclave_size;
|
||||
uint64_t tcs_offset;
|
||||
uint64_t initial_stack_offset;
|
||||
uint64_t tmp_rip;
|
||||
uint64_t sig_stack_low;
|
||||
uint64_t sig_stack_high;
|
||||
void* ecall_return_addr;
|
||||
void* ssa;
|
||||
sgx_pal_gpr_t* gpr;
|
||||
void* exit_target;
|
||||
void* fsbase;
|
||||
void* pre_ocall_stack;
|
||||
void* ustack_top;
|
||||
void* ustack;
|
||||
struct pal_handle_thread* thread;
|
||||
uint64_t ocall_exit_called;
|
||||
uint64_t thread_started;
|
||||
uint64_t ready_for_exceptions;
|
||||
uint64_t manifest_size;
|
||||
void* heap_min;
|
||||
void* heap_max;
|
||||
void* exec_addr;
|
||||
uint64_t exec_size;
|
||||
int* clear_child_tid;
|
||||
struct untrusted_area untrusted_area_cache;
|
||||
};
|
||||
|
||||
#ifndef DEBUG
|
||||
@@ -45,6 +52,11 @@ extern uint64_t dummy_debug_variable;
|
||||
#endif
|
||||
|
||||
# ifdef IN_ENCLAVE
|
||||
|
||||
static inline struct enclave_tls* get_tcb_trts(void) {
|
||||
return (struct enclave_tls*)pal_get_tcb();
|
||||
}
|
||||
|
||||
# define GET_ENCLAVE_TLS(member) \
|
||||
({ \
|
||||
struct enclave_tls * tmp; \
|
||||
|
||||
Reference in New Issue
Block a user