Skip to content

Fix BaseException escape from host callback trampoline (#336)#337

Merged
alexcrichton merged 1 commit intobytecodealliance:mainfrom
aallan:fix/baseexception-host-callback-336
May 7, 2026
Merged

Fix BaseException escape from host callback trampoline (#336)#337
alexcrichton merged 1 commit intobytecodealliance:mainfrom
aallan:fix/baseexception-host-callback-336

Conversation

@aallan
Copy link
Copy Markdown
Contributor

@aallan aallan commented May 7, 2026

Summary

Fixes #336.

The host-callback trampoline in wasmtime/_func.py caught Exception rather than BaseException, so BaseException subclasses (KeyboardInterrupt, SystemExit, custom BaseException subclasses) escaped the handler and caused the Python process to SIGABRT inside Rust's HostFunc::array_call_trampoline (libmalloc "pointer being freed was not allocated") rather than propagating to the Python caller.

The fix broadens the catch to BaseException. The existing LAST_EXCEPTION / Trap("python exception") machinery already handles re-raising on the Python side, so no other code changes are needed.

This is Option A from the issue — the minimum diff that closes the abort. Today, the original Python exception is re-raised at the call boundary via maybe_raise_last_exn, so after this fix KeyboardInterrupt from a host callback comes back as KeyboardInterrupt and ^C feels like ^C. Option B (catch BaseException but explicitly preserve KeyboardInterrupt/SystemExit semantics in some other way, or re-shape propagation) would be a behavioural decision; happy to send a follow-up PR if you'd prefer something different here. Flagging the choice for maintainer preference.

Test plan

Notes

  • Reproducer in BaseException raised in host import callback aborts the process (libmalloc) instead of propagating #336 was on macOS 15.7.3 + Python 3.14.3 + wasmtime-py 44.0.0. The bug is in pure-Python code and the C ABI contract, so it's platform-independent — macOS just gives the loudest crash because of libmalloc's poison-page guard. Linux glibc would also abort but the exact symptom may differ.
  • The annotation LAST_EXCEPTION: Optional[Exception] is now technically narrower than what we may store. Project mypy settings don't flag it; left untouched to keep the diff minimal, but happy to widen it if preferred.

…nce#336)

The host-callback trampoline in `wasmtime/_func.py` caught `Exception`,
which let `BaseException` subclasses (`KeyboardInterrupt`, `SystemExit`,
custom `BaseException` subclasses) escape into the Rust array_call
trampoline with an undefined `c_void_p` return value, causing a libmalloc
SIGABRT inside `HostFunc::array_call_trampoline`.

Broadens the catch to `BaseException`. The existing `LAST_EXCEPTION` /
`Trap("python exception")` machinery handles propagation back to the
Python caller unchanged.

Adds regression tests for `KeyboardInterrupt`, `SystemExit`, and a
custom `BaseException` subclass raised from host callbacks.

Fixes bytecodealliance#336
@alexcrichton
Copy link
Copy Markdown
Member

Looks good to me, thanks!

@alexcrichton alexcrichton merged commit 5c84f84 into bytecodealliance:main May 7, 2026
15 checks passed
@aallan aallan deleted the fix/baseexception-host-callback-336 branch May 7, 2026 14:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BaseException raised in host import callback aborts the process (libmalloc) instead of propagating

2 participants