|  | @@ -26,21 +26,72 @@ DEFINE_LISTP(heap_vma);
 | 
	
		
			
				|  |  |  static LISTP_TYPE(heap_vma) g_heap_vma_list = LISTP_INIT;
 | 
	
		
			
				|  |  |  static PAL_LOCK g_heap_vma_lock = LOCK_INIT;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +/* heap_vma objects are taken from pre-allocated pool to avoid recursive mallocs */
 | 
	
		
			
				|  |  | +#define MAX_HEAP_VMAS 100000
 | 
	
		
			
				|  |  | +static struct heap_vma g_heap_vma_pool[MAX_HEAP_VMAS];
 | 
	
		
			
				|  |  | +static size_t g_heap_vma_num = 0;
 | 
	
		
			
				|  |  | +static struct heap_vma* g_free_vma = NULL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* returns uninitialized heap_vma, the caller is responsible for setting at least bottom/top */
 | 
	
		
			
				|  |  | +static struct heap_vma* __alloc_vma(void) {
 | 
	
		
			
				|  |  | +    assert(_DkInternalIsLocked(&g_heap_vma_lock));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (g_free_vma) {
 | 
	
		
			
				|  |  | +        /* simple optimization: if there is a cached free vma object, use it */
 | 
	
		
			
				|  |  | +        assert((uintptr_t)g_free_vma >= (uintptr_t)&g_heap_vma_pool[0]);
 | 
	
		
			
				|  |  | +        assert((uintptr_t)g_free_vma <= (uintptr_t)&g_heap_vma_pool[MAX_HEAP_VMAS - 1]);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        struct heap_vma* ret = g_free_vma;
 | 
	
		
			
				|  |  | +        g_free_vma = NULL;
 | 
	
		
			
				|  |  | +        g_heap_vma_num++;
 | 
	
		
			
				|  |  | +        return ret;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* FIXME: this loop may become perf bottleneck on large number of vma objects; however,
 | 
	
		
			
				|  |  | +     * experiments show that this number typically does not exceed 20 (thanks to VMA merging) */
 | 
	
		
			
				|  |  | +    for (size_t i = 0; i < MAX_HEAP_VMAS; i++) {
 | 
	
		
			
				|  |  | +        if (!g_heap_vma_pool[i].bottom && !g_heap_vma_pool[i].top) {
 | 
	
		
			
				|  |  | +            /* found empty slot in the pool, use it */
 | 
	
		
			
				|  |  | +            g_heap_vma_num++;
 | 
	
		
			
				|  |  | +            return &g_heap_vma_pool[i];
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return NULL;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void __free_vma(struct heap_vma* vma) {
 | 
	
		
			
				|  |  | +    assert(_DkInternalIsLocked(&g_heap_vma_lock));
 | 
	
		
			
				|  |  | +    assert((uintptr_t)vma >= (uintptr_t)&g_heap_vma_pool[0]);
 | 
	
		
			
				|  |  | +    assert((uintptr_t)vma <= (uintptr_t)&g_heap_vma_pool[MAX_HEAP_VMAS - 1]);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    g_free_vma  = vma;
 | 
	
		
			
				|  |  | +    vma->top    = 0;
 | 
	
		
			
				|  |  | +    vma->bottom = 0;
 | 
	
		
			
				|  |  | +    g_heap_vma_num--;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  int init_enclave_pages(void) {
 | 
	
		
			
				|  |  | +    int ret;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      g_heap_bottom = pal_sec.heap_min;
 | 
	
		
			
				|  |  |      g_heap_top    = pal_sec.heap_max;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      size_t reserved_size = 0;
 | 
	
		
			
				|  |  |      struct heap_vma* exec_vma = NULL;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    _DkInternalLock(&g_heap_vma_lock);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      if (pal_sec.exec_addr < g_heap_top && pal_sec.exec_addr + pal_sec.exec_size > g_heap_bottom) {
 | 
	
		
			
				|  |  |          /* there is an executable mapped inside the heap, carve a VMA for its area; this can happen
 | 
	
		
			
				|  |  |           * in case of non-PIE executables that start at a predefined address (typically 0x400000) */
 | 
	
		
			
				|  |  | -        exec_vma = malloc(sizeof(*exec_vma));
 | 
	
		
			
				|  |  | +        exec_vma = __alloc_vma();
 | 
	
		
			
				|  |  |          if (!exec_vma) {
 | 
	
		
			
				|  |  |              SGX_DBG(DBG_E, "*** Cannot initialize VMA for executable ***\n");
 | 
	
		
			
				|  |  | -            return -PAL_ERROR_NOMEM;
 | 
	
		
			
				|  |  | +            ret = -PAL_ERROR_NOMEM;
 | 
	
		
			
				|  |  | +            goto out;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          exec_vma->bottom = SATURATED_P_SUB(pal_sec.exec_addr, MEMORY_GAP, g_heap_bottom);
 | 
	
		
			
				|  |  |          exec_vma->top = SATURATED_P_ADD(pal_sec.exec_addr + pal_sec.exec_size, MEMORY_GAP, g_heap_top);
 | 
	
		
			
				|  |  |          exec_vma->is_pal_internal = false;
 | 
	
	
		
			
				|  | @@ -53,7 +104,11 @@ int init_enclave_pages(void) {
 | 
	
		
			
				|  |  |      atomic_add(reserved_size / g_page_size, &g_alloced_pages);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      SGX_DBG(DBG_M, "Heap size: %luM\n", (g_heap_top - g_heap_bottom - reserved_size) / 1024 / 1024);
 | 
	
		
			
				|  |  | -    return 0;
 | 
	
		
			
				|  |  | +    ret = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +out:
 | 
	
		
			
				|  |  | +    _DkInternalUnlock(&g_heap_vma_lock);
 | 
	
		
			
				|  |  | +    return ret;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void* __create_vma_and_merge(void* addr, size_t size, bool is_pal_internal,
 | 
	
	
		
			
				|  | @@ -99,7 +154,7 @@ static void* __create_vma_and_merge(void* addr, size_t size, bool is_pal_interna
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      /* create VMA with [addr, addr+size); in case of existing overlapping VMAs, the created VMA is
 | 
	
		
			
				|  |  |       * merged with them and the old VMAs are discarded, similar to mmap(MAX_FIXED) */
 | 
	
		
			
				|  |  | -    struct heap_vma* vma = malloc(sizeof(*vma));
 | 
	
		
			
				|  |  | +    struct heap_vma* vma = __alloc_vma();
 | 
	
		
			
				|  |  |      if (!vma)
 | 
	
		
			
				|  |  |          return NULL;
 | 
	
		
			
				|  |  |      vma->bottom          = addr;
 | 
	
	
		
			
				|  | @@ -126,7 +181,7 @@ static void* __create_vma_and_merge(void* addr, size_t size, bool is_pal_interna
 | 
	
		
			
				|  |  |          vma->top    = MAX(vma_above->top, vma->top);
 | 
	
		
			
				|  |  |          LISTP_DEL(vma_above, &g_heap_vma_list, list);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        free(vma_above);
 | 
	
		
			
				|  |  | +        __free_vma(vma_above);
 | 
	
		
			
				|  |  |          vma_above = vma_above_above;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -143,7 +198,7 @@ static void* __create_vma_and_merge(void* addr, size_t size, bool is_pal_interna
 | 
	
		
			
				|  |  |          vma->top    = MAX(vma_below->top, vma->top);
 | 
	
		
			
				|  |  |          LISTP_DEL(vma_below, &g_heap_vma_list, list);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        free(vma_below);
 | 
	
		
			
				|  |  | +        __free_vma(vma_below);
 | 
	
		
			
				|  |  |          vma_below = vma_below_below;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -267,7 +322,7 @@ int free_enclave_pages(void* addr, size_t size) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          if (vma->bottom < addr) {
 | 
	
		
			
				|  |  |              /* create VMA [vma->bottom, addr); this may leave VMA [addr + size, vma->top), see below */
 | 
	
		
			
				|  |  | -            struct heap_vma* new = malloc(sizeof(*new));
 | 
	
		
			
				|  |  | +            struct heap_vma* new = __alloc_vma();
 | 
	
		
			
				|  |  |              if (!new) {
 | 
	
		
			
				|  |  |                  SGX_DBG(DBG_E, "*** Cannot create split VMA during free of address %p ***\n", addr);
 | 
	
		
			
				|  |  |                  ret = -PAL_ERROR_NOMEM;
 | 
	
	
		
			
				|  | @@ -285,7 +340,7 @@ int free_enclave_pages(void* addr, size_t size) {
 | 
	
		
			
				|  |  |          if (vma->top <= addr + size) {
 | 
	
		
			
				|  |  |              /* memory area to free completely covers/extends above the rest of the VMA */
 | 
	
		
			
				|  |  |              LISTP_DEL(vma, &g_heap_vma_list, list);
 | 
	
		
			
				|  |  | -            free(vma);
 | 
	
		
			
				|  |  | +            __free_vma(vma);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 |