From 851a19acc1e7b41f0ffd6fa74e680e404b3cfe66 Mon Sep 17 00:00:00 2001 From: Nunuhara Cabbage Date: Sat, 20 Feb 2021 20:41:05 -0800 Subject: [PATCH] Work around for FFI heap reallocation bugs ref types are currently passed to HLL functions as pointers into the heap. If the HLL function itself needs to allocate memory from the heap, it can cause the heap to be reallocated, rendering these heap pointers invalid. In order to work around this, hll_call now guarantees 64 slots are available on the heap before calling any HLL function. This is not a great solution, but it should work to the extent that HLL functions only allocate a fixed number of heap slots < 64. This bug could be easily triggered by scrolling up on the message log in Sengoku Rance. --- include/vm/heap.h | 11 +++++++++++ src/ffi.c | 5 +++++ src/heap.c | 46 ++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/include/vm/heap.h b/include/vm/heap.h index 5c0f06d..b692745 100644 --- a/include/vm/heap.h +++ b/include/vm/heap.h @@ -58,10 +58,21 @@ void exit_unref(int slot); bool heap_index_valid(int index); bool page_index_valid(int index); +bool string_index_valid(int index); struct page *heap_get_page(int index); +struct string *heap_get_string(int index); void heap_set_page(int slot, struct page *page); +/* + * Guarantee `headroom` free slots are available on the heap. + * + * XXX: this is a hack to fix an issue with HLL calls where a pointer into the + * heap can be made invalid by a heap reallocation triggered within the + * body of the HLL function. + */ +void heap_guarantee(unsigned headroom); + #ifdef VM_PRIVATE extern int32_t *heap_free_stack; diff --git a/src/ffi.c b/src/ffi.c index 7403f09..2f8c03b 100644 --- a/src/ffi.c +++ b/src/ffi.c @@ -54,6 +54,11 @@ void hll_call(int libno, int fno) if (!fun->fun) VM_ERROR("Unimplemented HLL function: %s.%s", ain->libraries[libno].name, f->name); + // XXX: Try to prevent the heap from being reallocated mid-call. + // This only works to the extent that HLL functions can guarantee + // no more than 64 heap allocations occur within the call... + heap_guarantee(64); + void *args[HLL_MAX_ARGS]; void *ptrs[HLL_MAX_ARGS]; for (int i = f->nr_arguments - 1; i >= 0; i--) { diff --git a/src/heap.c b/src/heap.c index 3c28896..4f7c60a 100644 --- a/src/heap.c +++ b/src/heap.c @@ -45,6 +45,30 @@ static const char *vm_ptrtype_string(enum vm_pointer_type type) { return "INVALID POINTER TYPE"; } +static void heap_grow(size_t new_size) +{ + heap = xrealloc(heap, sizeof(struct vm_pointer) * new_size); + heap_free_stack = xrealloc(heap_free_stack, sizeof(int32_t) * new_size); + for (size_t i = heap_size; i < new_size; i++) { + heap[i].ref = 0; + heap_free_stack[i] = i; + } + heap_size = new_size; +} + +void heap_guarantee(unsigned headroom) +{ + if (heap_size - heap_free_ptr >= headroom) + return; + + size_t new_size = heap_size; + while (new_size - heap_free_ptr < headroom) { + new_size += HEAP_ALLOC_STEP; + } + + heap_grow(new_size); +} + void heap_init(void) { if (!heap) { @@ -63,15 +87,8 @@ void heap_init(void) int32_t heap_alloc_slot(enum vm_pointer_type type) { - // grow heap if needed if (heap_free_ptr >= heap_size) { - heap = xrealloc(heap, sizeof(struct vm_pointer) * (heap_size+HEAP_ALLOC_STEP)); - heap_free_stack = xrealloc(heap_free_stack, sizeof(int32_t) * (heap_size+HEAP_ALLOC_STEP)); - for (size_t i = heap_size; i < heap_size+HEAP_ALLOC_STEP; i++) { - heap[i].ref = 0; - heap_free_stack[i] = i; - } - heap_size += HEAP_ALLOC_STEP; + heap_grow(heap_size+HEAP_ALLOC_STEP); } int32_t slot = heap_free_stack[heap_free_ptr++]; @@ -80,6 +97,7 @@ int32_t heap_alloc_slot(enum vm_pointer_type type) #ifdef DEBUG_HEAP heap[slot].alloc_addr = instr_ptr; heap[slot].ref_addr = 0; + heap[slot].free_addr = 0; #endif return slot; } @@ -183,6 +201,11 @@ bool page_index_valid(int index) return heap_index_valid(index) && heap[index].type == VM_PAGE; } +bool string_index_valid(int index) +{ + return heap_index_valid(index) && heap[index].type == VM_STRING; +} + struct page *heap_get_page(int index) { if (!page_index_valid(index)) @@ -190,6 +213,13 @@ struct page *heap_get_page(int index) return heap[index].page; } +struct string *heap_get_string(int index) +{ + if (!string_index_valid(index)) + VM_ERROR("Invalid string index: %d", index); + return heap[index].s; +} + void heap_set_page(int slot, struct page *page) { heap[slot].page = page;