summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--main.c8
-rw-r--r--tests/cram/test_basic.t6
-rw-r--r--tests/custom/04_bugs/35_vm_callframe_double_free36
-rw-r--r--vm.c18
4 files changed, 57 insertions, 11 deletions
diff --git a/main.c b/main.c
index e066961..7fd50d9 100644
--- a/main.c
+++ b/main.c
@@ -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 --
diff --git a/vm.c b/vm.c
index 0c756d7..7d4c10e 100644
--- a/vm.c
+++ b/vm.c
@@ -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);
}