diff options
author | Felix Fietkau <nbd@nbd.name> | 2025-02-06 14:35:06 +0100 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2025-02-10 10:42:43 +0100 |
commit | b32321beb112cfbca54554f9c685cd34bbbee0e4 (patch) | |
tree | d4d896e41976b40d20966b924afba9805db8654f | |
parent | 4bc1549bccccd13eaf759a403333804de5610528 (diff) |
vm: fix crash due to stale frame pointer
In some cases, calls made while processing insns in uc_vm_execute_chunk can
lead to realloc of vm->callframes without reloading the current frame pointer.
This was observed with I_ADD (which can call an object's tostring method).
Instead of playing whac-a-mole with insns affecting the frame pointer, let's
reload it whenever necessary.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
[further refactor to avoid redundant null pointer checks, fix native function
callframe stop condition while looking for exception handler]
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
-rw-r--r-- | vm.c | 36 |
1 files changed, 17 insertions, 19 deletions
@@ -2771,14 +2771,20 @@ uc_vm_signal_dispatch(uc_vm_t *vm) static uc_vm_status_t uc_vm_execute_chunk(uc_vm_t *vm) { - uc_callframe_t *frame = uc_vm_current_frame(vm); - uc_chunk_t *chunk = uc_vm_frame_chunk(frame); + uc_callframe_t *frame = NULL; + uc_chunk_t *chunk = NULL; size_t caller = vm->callframes.count - 1; uc_value_t *retval; uc_vm_insn_t insn; uint8_t *ip; - while (chunk && vm->callframes.count > caller) { + while (vm->callframes.count > caller) { + frame = &vm->callframes.entries[vm->callframes.count - 1]; + chunk = uc_vm_frame_chunk(frame); + + if (!chunk) + break; + if (vm->trace) { ip = frame->ip; insn = uc_vm_decode_insn(vm, frame, chunk); @@ -2968,15 +2974,11 @@ uc_vm_execute_chunk(uc_vm_t *vm) case I_CALL: case I_QCALL: uc_vm_insn_call(vm, insn); - frame = uc_vm_current_frame(vm); - chunk = frame->closure ? uc_vm_frame_chunk(frame) : NULL; break; case I_MCALL: case I_QMCALL: uc_vm_insn_mcall(vm, insn); - frame = uc_vm_current_frame(vm); - chunk = frame->closure ? uc_vm_frame_chunk(frame) : NULL; break; case I_RETURN: @@ -2986,9 +2988,6 @@ uc_vm_execute_chunk(uc_vm_t *vm) if (vm->callframes.count == 0) return STATUS_OK; - - frame = uc_vector_last(&vm->callframes); - chunk = uc_vm_frame_chunk(frame); break; case I_PRINT: @@ -3028,21 +3027,20 @@ exception: /* walk up callframes until something handles the exception or the original caller is reached */ while (!uc_vm_handle_exception(vm)) { + /* no further callframe, report unhandled exception and terminate */ + if (vm->callframes.count == 0) + return ERROR_RUNTIME; + /* if VM returned into native function, don't bubble up */ - if (!chunk) + if (!vm->callframes.entries[vm->callframes.count - 1].closure) return ERROR_RUNTIME; /* no exception handler in current function, pop callframe */ - if (vm->callframes.count > 0) - ucv_put(uc_vm_callframe_pop(vm)); + ucv_put(uc_vm_callframe_pop(vm)); - /* no further callframe, report unhandled exception and terminate */ - if (vm->callframes.count == 0 || vm->callframes.count <= caller) + /* do not bubble past original call depth */ + if (vm->callframes.count <= caller) return ERROR_RUNTIME; - - /* resume execution in next remaining callframe */ - frame = uc_vector_last(&vm->callframes); - chunk = uc_vm_frame_chunk(frame); } } |