diff --git a/tests/test_func.py b/tests/test_func.py index 00be6c3f..ef7ddb8e 100644 --- a/tests/test_func.py +++ b/tests/test_func.py @@ -85,6 +85,39 @@ def do_raise(): with self.assertRaises(Exception, msg="hello"): func(store) + def test_host_base_exception(self): + # Regression test for #336: a BaseException subclass raised from a + # host callback (KeyboardInterrupt, SystemExit, custom BaseException + # subclasses) used to escape the trampoline's `except Exception` + # handler and abort the process inside Rust's array_call_trampoline + # with a libmalloc SIGABRT. It must propagate cleanly to the caller. + store = Store() + ty = FuncType([], []) + + def raise_keyboard_interrupt(): + raise KeyboardInterrupt + + func = Func(store, ty, raise_keyboard_interrupt) + with self.assertRaises(KeyboardInterrupt): + func(store) + + def raise_system_exit(): + raise SystemExit(0) + + func = Func(store, ty, raise_system_exit) + with self.assertRaises(SystemExit): + func(store) + + class CustomBaseException(BaseException): + pass + + def raise_custom(): + raise CustomBaseException("custom") + + func = Func(store, ty, raise_custom) + with self.assertRaises(CustomBaseException): + func(store) + def test_type(self): store = Store() i32 = ValType.i32() diff --git a/wasmtime/_func.py b/wasmtime/_func.py index ccfdbf3c..64623aa4 100644 --- a/wasmtime/_func.py +++ b/wasmtime/_func.py @@ -209,7 +209,7 @@ def trampoline(idx, caller, params, nparams, results, nresults): # type: ignore for i, result in enumerate(pyresults): results[i] = Val._convert_to_raw(caller, result_tys[i], result) return 0 - except Exception as e: + except BaseException as e: global LAST_EXCEPTION LAST_EXCEPTION = e trap = Trap("python exception")._consume()