diff options
author | Jo-Philipp Wich <jo@mein.io> | 2022-04-06 20:22:16 +0200 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2022-04-07 15:13:02 +0200 |
commit | 2b59140b2c00987b9d8c2c908d2d44c67786e71e (patch) | |
tree | ab8093e9997a4c2b10772e3e8f600c27f9ff0858 | |
parent | 7d7e95006fa70e3f348ce118e756207d57040d93 (diff) |
vm: fix callframe double free on unhanded exceptions
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.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
-rw-r--r-- | tests/custom/04_bugs/35_vm_callframe_double_free | 36 | ||||
-rw-r--r-- | vm.c | 6 |
2 files changed, 40 insertions, 2 deletions
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) |