diff options
-rw-r--r-- | main.c | 8 | ||||
-rw-r--r-- | tests/cram/test_basic.t | 6 | ||||
-rw-r--r-- | tests/custom/04_bugs/35_vm_callframe_double_free | 36 | ||||
-rw-r--r-- | vm.c | 18 |
4 files changed, 57 insertions, 11 deletions
@@ -435,7 +435,7 @@ parse_library_load(char *opt, uc_vm_t *vm) ucv_put(lib); if (!ctx) - return false; + return vm->exception.type == EXCEPTION_NONE; ucv_object_add(uc_vm_scope_get(vm), name ? name : p, ctx); @@ -557,7 +557,11 @@ main(int argc, char **argv) break; case 'l': - parse_library_load(optarg, &vm); + if (!parse_library_load(optarg, &vm)) { + rv = 1; + goto out; + } + break; case 'c': diff --git a/tests/cram/test_basic.t b/tests/cram/test_basic.t index b85167f..7308002 100644 --- a/tests/cram/test_basic.t +++ b/tests/cram/test_basic.t @@ -79,19 +79,19 @@ check that ucode prints greetings: check that ucode provides proper error messages: - $ ucode -l foo + $ touch lib.uc; ucode -l lib Require either -e expression or source file [1] $ ucode -l foo -e ' ' Runtime error: No module named 'foo' could be found - [254] + [1] $ touch moo; ucode -l foo moo Runtime error: No module named 'foo' could be found - [254] + [1] check that ucode can load fs module: diff --git a/tests/custom/04_bugs/35_vm_callframe_double_free b/tests/custom/04_bugs/35_vm_callframe_double_free new file mode 100644 index 0000000..bb816eb --- /dev/null +++ b/tests/custom/04_bugs/35_vm_callframe_double_free @@ -0,0 +1,36 @@ +When invoking a native function as toplevel VM call which indirectly +triggers an unhandled exception in managed code, the callframes are +completely reset before the C function returns, leading to invalid +memory accesses when `uc_vm_call_native()` subsequently popped it's +own callframe again. + +This issue did not surface by executing script code through the +interpreter since in this case the VM will always execute a managed +code as toplevel call, but it could be triggered by invoking a native +function triggering an exception through the C API using `uc_vm_call()` +on a fresh `uc_vm_t` context or by utilizing the CLI interpreters `-l` +flag to preload a native code library triggering an exception. + + +-- File ex.uc -- +die("Exception"); +-- End -- + +-- Args -- +-L files/ -l ex +-- End -- + +-- Expect stderr -- +Exception +In main(), file files/ex.uc, line 1, byte 16: + called from anonymous function ([C]) + + `die("Exception");` + Near here -----^ + + +-- End -- + +-- Testcase -- +not reached +-- End -- @@ -435,8 +435,10 @@ uc_vm_call_native(uc_vm_t *vm, uc_value_t *ctx, uc_cfunction_t *fptr, bool mcall res = fptr->cfn(vm, nargs); - /* reset stack */ - ucv_put(uc_vm_callframe_pop(vm)); + /* Reset stack, check for callframe depth since an uncatched exception in managed + * code executed by fptr->cfn() could've reset the callframe stack already. */ + if (vm->callframes.count > 0) + ucv_put(uc_vm_callframe_pop(vm)); /* push return value */ if (!vm->exception.type) @@ -2600,9 +2602,6 @@ uc_vm_execute_chunk(uc_vm_t *vm) if (vm->callframes.count <= 1) { uc_vm_reset_callframes(vm); - if (vm->exhandler) - vm->exhandler(vm, &vm->exception); - return ERROR_RUNTIME; } @@ -2675,6 +2674,9 @@ uc_vm_execute(uc_vm_t *vm, uc_program_t *program, uc_value_t **retval) break; default: + if (vm->exhandler) + vm->exhandler(vm, &vm->exception); + if (retval) *retval = NULL; @@ -2739,8 +2741,12 @@ uc_vm_invoke(uc_vm_t *vm, const char *fname, size_t nargs, ...) ex = uc_vm_call(vm, false, nargs); - if (ex) + if (ex) { + if (vm->exhandler) + vm->exhandler(vm, &vm->exception); + return NULL; + } return uc_vm_stack_pop(vm); } |