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.
This commit is contained in:
Nunuhara Cabbage
2021-02-20 20:41:05 -08:00
parent 899e928128
commit 851a19acc1
3 changed files with 54 additions and 8 deletions
+11
View File
@@ -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;
+5
View File
@@ -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--) {
+38 -8
View File
@@ -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;