Add compilation flag to enable/disable heap corruption check (#2766)

Heap corruption check in ems memory allocator is enabled by default
to improve the security, but it may impact the performance a lot, this
PR adds cmake variable and compiler flag to enable/disable it.
This commit is contained in:
Wenyong Huang 2023-11-15 17:20:50 +08:00 committed by GitHub
parent fc03bc073e
commit 40d33d806b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 88 additions and 4 deletions

View File

@ -315,6 +315,11 @@
#define BH_ENABLE_GC_VERIFY 0 #define BH_ENABLE_GC_VERIFY 0
#endif #endif
/* Heap corruption check, enabled by default */
#ifndef BH_ENABLE_GC_CORRUPTION_CHECK
#define BH_ENABLE_GC_CORRUPTION_CHECK 1
#endif
/* Enable global heap pool if heap verification is enabled */ /* Enable global heap pool if heap verification is enabled */
#if BH_ENABLE_GC_VERIFY != 0 #if BH_ENABLE_GC_VERIFY != 0
#define WASM_ENABLE_GLOBAL_HEAP_POOL 1 #define WASM_ENABLE_GLOBAL_HEAP_POOL 1

View File

@ -24,19 +24,23 @@ hmu_is_in_heap(void *hmu, gc_uint8 *heap_base_addr, gc_uint8 *heap_end_addr)
static bool static bool
remove_tree_node(gc_heap_t *heap, hmu_tree_node_t *p) remove_tree_node(gc_heap_t *heap, hmu_tree_node_t *p)
{ {
hmu_tree_node_t *q = NULL, **slot = NULL, *parent; hmu_tree_node_t *q = NULL, **slot = NULL;
hmu_tree_node_t *root = heap->kfc_tree_root; #if BH_ENABLE_GC_CORRUPTION_CHECK != 0
hmu_tree_node_t *root = heap->kfc_tree_root, *parent;
gc_uint8 *base_addr = heap->base_addr; gc_uint8 *base_addr = heap->base_addr;
gc_uint8 *end_addr = base_addr + heap->current_size; gc_uint8 *end_addr = base_addr + heap->current_size;
#endif
bh_assert(p); bh_assert(p);
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
parent = p->parent; parent = p->parent;
if (!parent || p == root /* p can not be the ROOT node */ if (!parent || p == root /* p can not be the ROOT node */
|| !hmu_is_in_heap(p, base_addr, end_addr) || !hmu_is_in_heap(p, base_addr, end_addr)
|| (parent != root && !hmu_is_in_heap(parent, base_addr, end_addr))) { || (parent != root && !hmu_is_in_heap(parent, base_addr, end_addr))) {
goto fail; goto fail;
} }
#endif
/* get the slot which holds pointer to node p */ /* get the slot which holds pointer to node p */
if (p == p->parent->right) { if (p == p->parent->right) {
@ -67,9 +71,11 @@ remove_tree_node(gc_heap_t *heap, hmu_tree_node_t *p)
/* move right child up*/ /* move right child up*/
*slot = p->right; *slot = p->right;
if (p->right) { if (p->right) {
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(p->right, base_addr, end_addr)) { if (!hmu_is_in_heap(p->right, base_addr, end_addr)) {
goto fail; goto fail;
} }
#endif
p->right->parent = p->parent; p->right->parent = p->parent;
} }
@ -80,9 +86,11 @@ remove_tree_node(gc_heap_t *heap, hmu_tree_node_t *p)
if (!p->right) { if (!p->right) {
/* move left child up*/ /* move left child up*/
*slot = p->left; *slot = p->left;
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(p->left, base_addr, end_addr)) { if (!hmu_is_in_heap(p->left, base_addr, end_addr)) {
goto fail; goto fail;
} }
#endif
/* p->left can never be NULL unless it is corrupted. */ /* p->left can never be NULL unless it is corrupted. */
p->left->parent = p->parent; p->left->parent = p->parent;
@ -92,14 +100,18 @@ remove_tree_node(gc_heap_t *heap, hmu_tree_node_t *p)
/* both left & right exist, find p's predecessor at first*/ /* both left & right exist, find p's predecessor at first*/
q = p->left; q = p->left;
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(q, base_addr, end_addr)) { if (!hmu_is_in_heap(q, base_addr, end_addr)) {
goto fail; goto fail;
} }
#endif
while (q->right) { while (q->right) {
q = q->right; q = q->right;
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(q, base_addr, end_addr)) { if (!hmu_is_in_heap(q, base_addr, end_addr)) {
goto fail; goto fail;
} }
#endif
} }
/* remove from the tree*/ /* remove from the tree*/
@ -111,15 +123,19 @@ remove_tree_node(gc_heap_t *heap, hmu_tree_node_t *p)
q->left = p->left; q->left = p->left;
q->right = p->right; q->right = p->right;
if (q->left) { if (q->left) {
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(q->left, base_addr, end_addr)) { if (!hmu_is_in_heap(q->left, base_addr, end_addr)) {
goto fail; goto fail;
} }
#endif
q->left->parent = q; q->left->parent = q;
} }
if (q->right) { if (q->right) {
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(q->right, base_addr, end_addr)) { if (!hmu_is_in_heap(q->right, base_addr, end_addr)) {
goto fail; goto fail;
} }
#endif
q->right->parent = q; q->right->parent = q;
} }
@ -127,27 +143,35 @@ remove_tree_node(gc_heap_t *heap, hmu_tree_node_t *p)
return true; return true;
fail: fail:
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
heap->is_heap_corrupted = true; heap->is_heap_corrupted = true;
#endif
return false; return false;
} }
static bool static bool
unlink_hmu(gc_heap_t *heap, hmu_t *hmu) unlink_hmu(gc_heap_t *heap, hmu_t *hmu)
{ {
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
gc_uint8 *base_addr, *end_addr; gc_uint8 *base_addr, *end_addr;
#endif
gc_size_t size; gc_size_t size;
bh_assert(gci_is_heap_valid(heap)); bh_assert(gci_is_heap_valid(heap));
bh_assert(hmu && (gc_uint8 *)hmu >= heap->base_addr bh_assert(hmu && (gc_uint8 *)hmu >= heap->base_addr
&& (gc_uint8 *)hmu < heap->base_addr + heap->current_size); && (gc_uint8 *)hmu < heap->base_addr + heap->current_size);
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (hmu_get_ut(hmu) != HMU_FC) { if (hmu_get_ut(hmu) != HMU_FC) {
heap->is_heap_corrupted = true; heap->is_heap_corrupted = true;
return false; return false;
} }
#endif
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
base_addr = heap->base_addr; base_addr = heap->base_addr;
end_addr = base_addr + heap->current_size; end_addr = base_addr + heap->current_size;
#endif
size = hmu_get_size(hmu); size = hmu_get_size(hmu);
if (HMU_IS_FC_NORMAL(size)) { if (HMU_IS_FC_NORMAL(size)) {
@ -156,10 +180,12 @@ unlink_hmu(gc_heap_t *heap, hmu_t *hmu)
hmu_normal_node_t *node = heap->kfc_normal_list[node_idx].next; hmu_normal_node_t *node = heap->kfc_normal_list[node_idx].next;
while (node) { while (node) {
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(node, base_addr, end_addr)) { if (!hmu_is_in_heap(node, base_addr, end_addr)) {
heap->is_heap_corrupted = true; heap->is_heap_corrupted = true;
return false; return false;
} }
#endif
node_next = get_hmu_normal_node_next(node); node_next = get_hmu_normal_node_next(node);
if ((hmu_t *)node == hmu) { if ((hmu_t *)node == hmu) {
if (!node_prev) /* list head */ if (!node_prev) /* list head */
@ -205,7 +231,9 @@ hmu_set_free_size(hmu_t *hmu)
bool bool
gci_add_fc(gc_heap_t *heap, hmu_t *hmu, gc_size_t size) gci_add_fc(gc_heap_t *heap, hmu_t *hmu, gc_size_t size)
{ {
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
gc_uint8 *base_addr, *end_addr; gc_uint8 *base_addr, *end_addr;
#endif
hmu_normal_node_t *np = NULL; hmu_normal_node_t *np = NULL;
hmu_tree_node_t *root = NULL, *tp = NULL, *node = NULL; hmu_tree_node_t *root = NULL, *tp = NULL, *node = NULL;
uint32 node_idx; uint32 node_idx;
@ -219,8 +247,10 @@ gci_add_fc(gc_heap_t *heap, hmu_t *hmu, gc_size_t size)
<= heap->base_addr + heap->current_size); <= heap->base_addr + heap->current_size);
bh_assert(!(size & 7)); bh_assert(!(size & 7));
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
base_addr = heap->base_addr; base_addr = heap->base_addr;
end_addr = base_addr + heap->current_size; end_addr = base_addr + heap->current_size;
#endif
hmu_set_ut(hmu, HMU_FC); hmu_set_ut(hmu, HMU_FC);
hmu_set_size(hmu, size); hmu_set_size(hmu, size);
@ -228,10 +258,12 @@ gci_add_fc(gc_heap_t *heap, hmu_t *hmu, gc_size_t size)
if (HMU_IS_FC_NORMAL(size)) { if (HMU_IS_FC_NORMAL(size)) {
np = (hmu_normal_node_t *)hmu; np = (hmu_normal_node_t *)hmu;
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(np, base_addr, end_addr)) { if (!hmu_is_in_heap(np, base_addr, end_addr)) {
heap->is_heap_corrupted = true; heap->is_heap_corrupted = true;
return false; return false;
} }
#endif
node_idx = size >> 3; node_idx = size >> 3;
set_hmu_normal_node_next(np, heap->kfc_normal_list[node_idx].next); set_hmu_normal_node_next(np, heap->kfc_normal_list[node_idx].next);
@ -265,10 +297,12 @@ gci_add_fc(gc_heap_t *heap, hmu_t *hmu, gc_size_t size)
} }
tp = tp->left; tp = tp->left;
} }
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(tp, base_addr, end_addr)) { if (!hmu_is_in_heap(tp, base_addr, end_addr)) {
heap->is_heap_corrupted = true; heap->is_heap_corrupted = true;
return false; return false;
} }
#endif
} }
return true; return true;
} }
@ -321,15 +355,19 @@ alloc_hmu(gc_heap_t *heap, gc_size_t size)
bh_assert(node_idx >= init_node_idx); bh_assert(node_idx >= init_node_idx);
p = normal_head->next; p = normal_head->next;
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(p, base_addr, end_addr)) { if (!hmu_is_in_heap(p, base_addr, end_addr)) {
heap->is_heap_corrupted = true; heap->is_heap_corrupted = true;
return NULL; return NULL;
} }
#endif
normal_head->next = get_hmu_normal_node_next(p); normal_head->next = get_hmu_normal_node_next(p);
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (((gc_int32)(uintptr_t)hmu_to_obj(p) & 7) != 0) { if (((gc_int32)(uintptr_t)hmu_to_obj(p) & 7) != 0) {
heap->is_heap_corrupted = true; heap->is_heap_corrupted = true;
return NULL; return NULL;
} }
#endif
if ((gc_size_t)node_idx != (uint32)init_node_idx if ((gc_size_t)node_idx != (uint32)init_node_idx
/* with bigger size*/ /* with bigger size*/
@ -365,10 +403,12 @@ alloc_hmu(gc_heap_t *heap, gc_size_t size)
bh_assert(root); bh_assert(root);
tp = root->right; tp = root->right;
while (tp) { while (tp) {
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(tp, base_addr, end_addr)) { if (!hmu_is_in_heap(tp, base_addr, end_addr)) {
heap->is_heap_corrupted = true; heap->is_heap_corrupted = true;
return NULL; return NULL;
} }
#endif
if (tp->size < size) { if (tp->size < size) {
tp = tp->right; tp = tp->right;
@ -462,10 +502,12 @@ gc_alloc_vo_internal(void *vheap, gc_size_t size, const char *file, int line)
/* integer overflow */ /* integer overflow */
return NULL; return NULL;
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (heap->is_heap_corrupted) { if (heap->is_heap_corrupted) {
os_printf("[GC_ERROR]Heap is corrupted, allocate memory failed.\n"); os_printf("[GC_ERROR]Heap is corrupted, allocate memory failed.\n");
return NULL; return NULL;
} }
#endif
os_mutex_lock(&heap->lock); os_mutex_lock(&heap->lock);
@ -522,10 +564,12 @@ gc_realloc_vo_internal(void *vheap, void *ptr, gc_size_t size, const char *file,
/* integer overflow */ /* integer overflow */
return NULL; return NULL;
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (heap->is_heap_corrupted) { if (heap->is_heap_corrupted) {
os_printf("[GC_ERROR]Heap is corrupted, allocate memory failed.\n"); os_printf("[GC_ERROR]Heap is corrupted, allocate memory failed.\n");
return NULL; return NULL;
} }
#endif
if (obj_old) { if (obj_old) {
hmu_old = obj_to_hmu(obj_old); hmu_old = obj_to_hmu(obj_old);
@ -647,10 +691,12 @@ gc_free_vo_internal(void *vheap, gc_object_t obj, const char *file, int line)
return GC_SUCCESS; return GC_SUCCESS;
} }
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (heap->is_heap_corrupted) { if (heap->is_heap_corrupted) {
os_printf("[GC_ERROR]Heap is corrupted, free memory failed.\n"); os_printf("[GC_ERROR]Heap is corrupted, free memory failed.\n");
return GC_ERROR; return GC_ERROR;
} }
#endif
hmu = obj_to_hmu(obj); hmu = obj_to_hmu(obj);
@ -767,11 +813,13 @@ gci_dump(gc_heap_t *heap)
else if (ut == HMU_FC) else if (ut == HMU_FC)
inuse = 'F'; inuse = 'F';
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (size == 0 || size > (uint32)((uint8 *)end - (uint8 *)cur)) { if (size == 0 || size > (uint32)((uint8 *)end - (uint8 *)cur)) {
os_printf("[GC_ERROR]Heap is corrupted, heap dump failed.\n"); os_printf("[GC_ERROR]Heap is corrupted, heap dump failed.\n");
heap->is_heap_corrupted = true; heap->is_heap_corrupted = true;
return; return;
} }
#endif
os_printf("#%d %08" PRIx32 " %" PRIx32 " %d %d" os_printf("#%d %08" PRIx32 " %" PRIx32 " %d %d"
" %c %" PRId32 "\n", " %c %" PRId32 "\n",
@ -788,8 +836,12 @@ gci_dump(gc_heap_t *heap)
i++; i++;
} }
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (cur != end) { if (cur != end) {
os_printf("[GC_ERROR]Heap is corrupted, heap dump failed.\n"); os_printf("[GC_ERROR]Heap is corrupted, heap dump failed.\n");
heap->is_heap_corrupted = true; heap->is_heap_corrupted = true;
} }
#else
bh_assert(cur == end);
#endif
} }

View File

@ -271,9 +271,11 @@ typedef struct gc_heap_struct {
size[left] <= size[cur] < size[right] */ size[left] <= size[cur] < size[right] */
hmu_tree_node_t *kfc_tree_root; hmu_tree_node_t *kfc_tree_root;
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
/* whether heap is corrupted, e.g. the hmu nodes are modified /* whether heap is corrupted, e.g. the hmu nodes are modified
by user */ by user */
bool is_heap_corrupted; bool is_heap_corrupted;
#endif
gc_size_t init_size; gc_size_t init_size;
gc_size_t highmark_size; gc_size_t highmark_size;

View File

@ -83,7 +83,9 @@ hmu_verify(void *vheap, hmu_t *hmu)
os_printf("Invalid padding for object created at %s:%d\n", os_printf("Invalid padding for object created at %s:%d\n",
(prefix->file_name ? prefix->file_name : ""), (prefix->file_name ? prefix->file_name : ""),
prefix->line_no); prefix->line_no);
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
heap->is_heap_corrupted = true; heap->is_heap_corrupted = true;
#endif
} }
} }
} }

View File

@ -133,8 +133,11 @@ gc_destroy_with_pool(gc_handle_t handle)
hmu_t *cur = (hmu_t *)heap->base_addr; hmu_t *cur = (hmu_t *)heap->base_addr;
hmu_t *end = (hmu_t *)((char *)heap->base_addr + heap->current_size); hmu_t *end = (hmu_t *)((char *)heap->base_addr + heap->current_size);
if (!heap->is_heap_corrupted if (
&& (hmu_t *)((char *)cur + hmu_get_size(cur)) != end) { #if BH_ENABLE_GC_CORRUPTION_CHECK != 0
!heap->is_heap_corrupted &&
#endif
(hmu_t *)((char *)cur + hmu_get_size(cur)) != end) {
os_printf("Memory leak detected:\n"); os_printf("Memory leak detected:\n");
gci_dump(heap); gci_dump(heap);
ret = GC_ERROR; ret = GC_ERROR;
@ -186,10 +189,12 @@ gc_migrate(gc_handle_t handle, char *pool_buf_new, gc_size_t pool_buf_size)
if (offset == 0) if (offset == 0)
return 0; return 0;
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (heap->is_heap_corrupted) { if (heap->is_heap_corrupted) {
os_printf("[GC_ERROR]Heap is corrupted, heap migrate failed.\n"); os_printf("[GC_ERROR]Heap is corrupted, heap migrate failed.\n");
return GC_ERROR; return GC_ERROR;
} }
#endif
heap->base_addr = (uint8 *)base_addr_new; heap->base_addr = (uint8 *)base_addr_new;
@ -211,11 +216,13 @@ gc_migrate(gc_handle_t handle, char *pool_buf_new, gc_size_t pool_buf_size)
while (cur < end) { while (cur < end) {
size = hmu_get_size(cur); size = hmu_get_size(cur);
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (size <= 0 || size > (uint32)((uint8 *)end - (uint8 *)cur)) { if (size <= 0 || size > (uint32)((uint8 *)end - (uint8 *)cur)) {
os_printf("[GC_ERROR]Heap is corrupted, heap migrate failed.\n"); os_printf("[GC_ERROR]Heap is corrupted, heap migrate failed.\n");
heap->is_heap_corrupted = true; heap->is_heap_corrupted = true;
return GC_ERROR; return GC_ERROR;
} }
#endif
if (hmu_get_ut(cur) == HMU_FC && !HMU_IS_FC_NORMAL(size)) { if (hmu_get_ut(cur) == HMU_FC && !HMU_IS_FC_NORMAL(size)) {
tree_node = (hmu_tree_node_t *)cur; tree_node = (hmu_tree_node_t *)cur;
@ -238,11 +245,15 @@ gc_migrate(gc_handle_t handle, char *pool_buf_new, gc_size_t pool_buf_size)
cur = (hmu_t *)((char *)cur + size); cur = (hmu_t *)((char *)cur + size);
} }
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (cur != end) { if (cur != end) {
os_printf("[GC_ERROR]Heap is corrupted, heap migrate failed.\n"); os_printf("[GC_ERROR]Heap is corrupted, heap migrate failed.\n");
heap->is_heap_corrupted = true; heap->is_heap_corrupted = true;
return GC_ERROR; return GC_ERROR;
} }
#else
bh_assert(cur == end);
#endif
return 0; return 0;
} }
@ -250,9 +261,13 @@ gc_migrate(gc_handle_t handle, char *pool_buf_new, gc_size_t pool_buf_size)
bool bool
gc_is_heap_corrupted(gc_handle_t handle) gc_is_heap_corrupted(gc_handle_t handle)
{ {
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
gc_heap_t *heap = (gc_heap_t *)handle; gc_heap_t *heap = (gc_heap_t *)handle;
return heap->is_heap_corrupted ? true : false; return heap->is_heap_corrupted ? true : false;
#else
return false;
#endif
} }
#if BH_ENABLE_GC_VERIFY != 0 #if BH_ENABLE_GC_VERIFY != 0

View File

@ -10,6 +10,14 @@ if (WAMR_BUILD_GC_VERIFY EQUAL 1)
add_definitions (-DBH_ENABLE_GC_VERIFY=1) add_definitions (-DBH_ENABLE_GC_VERIFY=1)
endif () endif ()
if (NOT DEFINED WAMR_BUILD_GC_CORRUPTION_CHECK)
set (WAMR_BUILD_GC_CORRUPTION_CHECK 1)
endif ()
if (WAMR_BUILD_GC_CORRUPTION_CHECK EQUAL 0)
add_definitions (-DBH_ENABLE_GC_CORRUPTION_CHECK=0)
endif ()
file (GLOB_RECURSE source_all file (GLOB_RECURSE source_all
${MEM_ALLOC_DIR}/ems/*.c ${MEM_ALLOC_DIR}/ems/*.c
${MEM_ALLOC_DIR}/tlsf/*.c ${MEM_ALLOC_DIR}/tlsf/*.c