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
#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 */
#if BH_ENABLE_GC_VERIFY != 0
#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
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 *root = heap->kfc_tree_root;
hmu_tree_node_t *q = NULL, **slot = NULL;
#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 *end_addr = base_addr + heap->current_size;
#endif
bh_assert(p);
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
parent = p->parent;
if (!parent || p == root /* p can not be the ROOT node */
|| !hmu_is_in_heap(p, base_addr, end_addr)
|| (parent != root && !hmu_is_in_heap(parent, base_addr, end_addr))) {
goto fail;
}
#endif
/* get the slot which holds pointer to node p */
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*/
*slot = p->right;
if (p->right) {
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(p->right, base_addr, end_addr)) {
goto fail;
}
#endif
p->right->parent = p->parent;
}
@ -80,9 +86,11 @@ remove_tree_node(gc_heap_t *heap, hmu_tree_node_t *p)
if (!p->right) {
/* move left child up*/
*slot = p->left;
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(p->left, base_addr, end_addr)) {
goto fail;
}
#endif
/* p->left can never be NULL unless it is corrupted. */
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*/
q = p->left;
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(q, base_addr, end_addr)) {
goto fail;
}
#endif
while (q->right) {
q = q->right;
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(q, base_addr, end_addr)) {
goto fail;
}
#endif
}
/* 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->right = p->right;
if (q->left) {
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(q->left, base_addr, end_addr)) {
goto fail;
}
#endif
q->left->parent = q;
}
if (q->right) {
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(q->right, base_addr, end_addr)) {
goto fail;
}
#endif
q->right->parent = q;
}
@ -127,27 +143,35 @@ remove_tree_node(gc_heap_t *heap, hmu_tree_node_t *p)
return true;
fail:
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
heap->is_heap_corrupted = true;
#endif
return false;
}
static bool
unlink_hmu(gc_heap_t *heap, hmu_t *hmu)
{
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
gc_uint8 *base_addr, *end_addr;
#endif
gc_size_t size;
bh_assert(gci_is_heap_valid(heap));
bh_assert(hmu && (gc_uint8 *)hmu >= heap->base_addr
&& (gc_uint8 *)hmu < heap->base_addr + heap->current_size);
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (hmu_get_ut(hmu) != HMU_FC) {
heap->is_heap_corrupted = true;
return false;
}
#endif
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
base_addr = heap->base_addr;
end_addr = base_addr + heap->current_size;
#endif
size = hmu_get_size(hmu);
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;
while (node) {
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(node, base_addr, end_addr)) {
heap->is_heap_corrupted = true;
return false;
}
#endif
node_next = get_hmu_normal_node_next(node);
if ((hmu_t *)node == hmu) {
if (!node_prev) /* list head */
@ -205,7 +231,9 @@ hmu_set_free_size(hmu_t *hmu)
bool
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;
#endif
hmu_normal_node_t *np = NULL;
hmu_tree_node_t *root = NULL, *tp = NULL, *node = NULL;
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);
bh_assert(!(size & 7));
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
base_addr = heap->base_addr;
end_addr = base_addr + heap->current_size;
#endif
hmu_set_ut(hmu, HMU_FC);
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)) {
np = (hmu_normal_node_t *)hmu;
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(np, base_addr, end_addr)) {
heap->is_heap_corrupted = true;
return false;
}
#endif
node_idx = size >> 3;
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;
}
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(tp, base_addr, end_addr)) {
heap->is_heap_corrupted = true;
return false;
}
#endif
}
return true;
}
@ -321,15 +355,19 @@ alloc_hmu(gc_heap_t *heap, gc_size_t size)
bh_assert(node_idx >= init_node_idx);
p = normal_head->next;
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(p, base_addr, end_addr)) {
heap->is_heap_corrupted = true;
return NULL;
}
#endif
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) {
heap->is_heap_corrupted = true;
return NULL;
}
#endif
if ((gc_size_t)node_idx != (uint32)init_node_idx
/* with bigger size*/
@ -365,10 +403,12 @@ alloc_hmu(gc_heap_t *heap, gc_size_t size)
bh_assert(root);
tp = root->right;
while (tp) {
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (!hmu_is_in_heap(tp, base_addr, end_addr)) {
heap->is_heap_corrupted = true;
return NULL;
}
#endif
if (tp->size < size) {
tp = tp->right;
@ -462,10 +502,12 @@ gc_alloc_vo_internal(void *vheap, gc_size_t size, const char *file, int line)
/* integer overflow */
return NULL;
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (heap->is_heap_corrupted) {
os_printf("[GC_ERROR]Heap is corrupted, allocate memory failed.\n");
return NULL;
}
#endif
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 */
return NULL;
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (heap->is_heap_corrupted) {
os_printf("[GC_ERROR]Heap is corrupted, allocate memory failed.\n");
return NULL;
}
#endif
if (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;
}
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (heap->is_heap_corrupted) {
os_printf("[GC_ERROR]Heap is corrupted, free memory failed.\n");
return GC_ERROR;
}
#endif
hmu = obj_to_hmu(obj);
@ -767,11 +813,13 @@ gci_dump(gc_heap_t *heap)
else if (ut == HMU_FC)
inuse = 'F';
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (size == 0 || size > (uint32)((uint8 *)end - (uint8 *)cur)) {
os_printf("[GC_ERROR]Heap is corrupted, heap dump failed.\n");
heap->is_heap_corrupted = true;
return;
}
#endif
os_printf("#%d %08" PRIx32 " %" PRIx32 " %d %d"
" %c %" PRId32 "\n",
@ -788,8 +836,12 @@ gci_dump(gc_heap_t *heap)
i++;
}
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (cur != end) {
os_printf("[GC_ERROR]Heap is corrupted, heap dump failed.\n");
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] */
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
by user */
bool is_heap_corrupted;
#endif
gc_size_t init_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",
(prefix->file_name ? prefix->file_name : ""),
prefix->line_no);
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
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 *end = (hmu_t *)((char *)heap->base_addr + heap->current_size);
if (!heap->is_heap_corrupted
&& (hmu_t *)((char *)cur + hmu_get_size(cur)) != end) {
if (
#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");
gci_dump(heap);
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)
return 0;
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (heap->is_heap_corrupted) {
os_printf("[GC_ERROR]Heap is corrupted, heap migrate failed.\n");
return GC_ERROR;
}
#endif
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) {
size = hmu_get_size(cur);
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (size <= 0 || size > (uint32)((uint8 *)end - (uint8 *)cur)) {
os_printf("[GC_ERROR]Heap is corrupted, heap migrate failed.\n");
heap->is_heap_corrupted = true;
return GC_ERROR;
}
#endif
if (hmu_get_ut(cur) == HMU_FC && !HMU_IS_FC_NORMAL(size)) {
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);
}
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
if (cur != end) {
os_printf("[GC_ERROR]Heap is corrupted, heap migrate failed.\n");
heap->is_heap_corrupted = true;
return GC_ERROR;
}
#else
bh_assert(cur == end);
#endif
return 0;
}
@ -250,9 +261,13 @@ gc_migrate(gc_handle_t handle, char *pool_buf_new, gc_size_t pool_buf_size)
bool
gc_is_heap_corrupted(gc_handle_t handle)
{
#if BH_ENABLE_GC_CORRUPTION_CHECK != 0
gc_heap_t *heap = (gc_heap_t *)handle;
return heap->is_heap_corrupted ? true : false;
#else
return false;
#endif
}
#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)
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
${MEM_ALLOC_DIR}/ems/*.c
${MEM_ALLOC_DIR}/tlsf/*.c