cfallin opened PR #13613 from cfallin:keep-your-exceptions-to-yourself to bytecodealliance:main:
Per the [Canonical ABI], an exception thrown in one component must not cross component boundaries; instead, it must become a trap. (Thanks to fitzgen for realizing this and raising the issue to me!)
This PR updates the adapter compiler (FACT) to wrap adapters in a
try_table, with acatch_allclause that raises a trap, when exceptions are enabled in the engine Wasm feature configuration. This is a clean and simple catch-all: because FACT-compiled adapters are Wasm modules, we can use Wasm exception handling itself and be assured that we will tie into the necessary runtime infra without having to change the internal throw logic to be component-aware at all.[Canonical ABI]: https://github.com/WebAssembly/component-model/blob/823aa133c2d94a328269da25fd9712bccc142d49/design/mvp/CanonicalABI.md?plain=1#L3768
<!--
Please make sure you include the following information:
If this work has been discussed elsewhere, please include a link to that
conversation. If it was discussed in an issue, just mention "issue #...".Explain why this change is needed. If the details are in an issue already,
this can be brief.Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.htmlPlease ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->
cfallin requested pchickey for a review on PR #13613.
cfallin requested wasmtime-core-reviewers for a review on PR #13613.
alexcrichton edited PR #13613:
Per the [Canonical ABI], an exception thrown in one component must not cross component boundaries; instead, it must become a trap. (Thanks to fitzgen for realizing this and raising the issue to me!)
This PR updates the adapter compiler (FACT) to wrap adapters in a
try_table, with acatch_allclause that raises a trap, when exceptions are enabled in the engine Wasm feature configuration. This is a clean and simple catch-all: because FACT-compiled adapters are Wasm modules, we can use Wasm exception handling itself and be assured that we will tie into the necessary runtime infra without having to change the internal throw logic to be component-aware at all.[Canonical ABI]: https://github.com/WebAssembly/component-model/blob/823aa133c2d94a328269da25fd9712bccc142d49/design/mvp/CanonicalABI.md?plain=1#L3768
Closes #13614
<!--
Please make sure you include the following information:
If this work has been discussed elsewhere, please include a link to that
conversation. If it was discussed in an issue, just mention "issue #...".Explain why this change is needed. If the details are in an issue already,
this can be brief.Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.htmlPlease ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->
cfallin updated PR #13613.
:memo: alexcrichton submitted PR review:
I think we'll want to expand out the test here to be more exhaustive since there are other locations this may need to get integrated. For example
realloc,post-return, and various async-related features I'm not sure will be caught by this current block/injection. I'll try to write up a test.
:speech_balloon: alexcrichton created PR review comment:
Could this be plumbed as
WasmFeaturesrather than aboolto help make ti a bit more self-documenting in case more params are grown here?
:speech_balloon: alexcrichton created PR review comment:
I'm wary about the non-local behavior of return here w.r.t. how everything else right now is structured as a relatively self-contained method in this compiler. Could this instead have the "didn't throw" case jump to an outermost block which continues execution past this? Something like:
block $ok (result ...) block $uncaught-exception try_table (catch_all $uncaught-exception) call $the-thing br $ok end unreachable end end ;; ... rest of the translation ...
:speech_balloon: alexcrichton created PR review comment:
Could this test be modeled as
*.wast?
:speech_balloon: alexcrichton created PR review comment:
Do you know where this
try_call_indirectis coming from? Is this perhaps a libcall invocation, and if so we probably want an issue about avoidingtry_call_*with libcalls because they can't raise exceptions.
:memo: cfallin submitted PR review.
:speech_balloon: cfallin created PR review comment:
Ah, yeah, this is general inlining logic I think: if we inline at a
try_calland in the inlinee, we have acall_indirect, it will turn into atry_call_indirect, because Cranelift doesn't know anything about callees' throws. That's required for soundness with the current IR, a Big Project to change (effect system in Cranelift signatures?) and I'm not sure there's a smaller point-fix...
:memo: cfallin submitted PR review.
:speech_balloon: cfallin created PR review comment:
(More precisely, this requires something like checked exceptions and a whole-program analysis: the original
call_indirectcould still indirectly call something that throws, per CLIF semantics. We'd need a separate "call that never even indirectly invokes a throw" instruction, i.e., a call with a checked-exception null set, and then somehow interprocedurally typecheck that.)
:speech_balloon: cfallin edited PR review comment.
cfallin updated PR #13613.
:memo: cfallin submitted PR review.
:speech_balloon: cfallin created PR review comment:
Converted!
:memo: cfallin submitted PR review.
:speech_balloon: cfallin created PR review comment:
Done!
:memo: cfallin submitted PR review.
:speech_balloon: cfallin created PR review comment:
Switched!
cfallin commented on PR #13613:
Thanks! Updated and added tests for
reallocandpost-returnthrowing as well.
cfallin updated PR #13613.
:memo: alexcrichton submitted PR review.
:speech_balloon: alexcrichton created PR review comment:
Ah ok yeah that makes sense, yeah. Taking a leaf out of Rust's book what I'd imagine is that a solution for this would be a bool on an ABI as to whether it could unwind or not, and that way
try_call-ing a nounwind ABI by definition can become acallinstead (or something like that). That's definitely out of scope for this PR though
:thumbs_up: alexcrichton submitted PR review:
er, meant for that to be this
alexcrichton commented on PR #13613:
Ok I tried to write up some more parts of a test case here, and what I've found is:
- This'll want to handle the guest-closes-guest-resource path (dtor throwing exception is still caught)
- For the async adapters I think you can remove the exception blocks. If you can include these tests though it'll ensure that if we ever optimize the fact codegen in the future it'll be caught and they can be added back in as necessary
cfallin requested fitzgen for a review on PR #13613.
cfallin requested wasmtime-compiler-reviewers for a review on PR #13613.
cfallin updated PR #13613.
cfallin updated PR #13613.
cfallin commented on PR #13613:
Updated, thanks! For
resource.drop, uncovered from new test, since that's a call emitted from the component compiler directly, that also needs a separatetry_call_indirect.
cfallin has enabled auto merge for PR #13613.
cfallin updated PR #13613.
cfallin added PR #13613 Component model: catch exceptions at component barriers and turn into traps. to the merge queue
github-merge-queue[bot] removed PR #13613 Component model: catch exceptions at component barriers and turn into traps. from the merge queue
cfallin added PR #13613 Component model: catch exceptions at component barriers and turn into traps. to the merge queue
github-merge-queue[bot] removed PR #13613 Component model: catch exceptions at component barriers and turn into traps. from the merge queue
github-actions[bot] added the label wasmtime:c-api on PR #13613.
alexcrichton added PR #13613 Component model: catch exceptions at component barriers and turn into traps. to the merge queue
github-merge-queue[bot] removed PR #13613 Component model: catch exceptions at component barriers and turn into traps. from the merge queue
alexcrichton added PR #13613 Component model: catch exceptions at component barriers and turn into traps. to the merge queue
github-merge-queue[bot] removed PR #13613 Component model: catch exceptions at component barriers and turn into traps. from the merge queue
alexcrichton added PR #13613 Component model: catch exceptions at component barriers and turn into traps. to the merge queue
github-merge-queue[bot] removed PR #13613 Component model: catch exceptions at component barriers and turn into traps. from the merge queue
alexcrichton commented on PR #13613:
Hm ok given how consistently this is failing on s390x this may not actually be spurious (e.g. #10000). Mind double-checking locally @cfallin and seeing if you can reproduce the fault?
cfallin commented on PR #13613:
Yep, I can sometimes (nondeterministically) reproduce locally -- digging in.
cfallin updated PR #13613.
cfallin commented on PR #13613:
@alexcrichton fixed as I missed the non-signals-based-trap case for the
resource.droptrap, which was a bit I added late above. Mind taking another look before merging?
cfallin commented on PR #13613:
(PR CI failing is crates.io being inaccessible from GitHub, as usual on a day ending in
-day)
:thumbs_up: alexcrichton submitted PR review.
alexcrichton has enabled auto merge for PR #13613.
alexcrichton added PR #13613 Component model: catch exceptions at component barriers and turn into traps. to the merge queue
github-merge-queue[bot] removed PR #13613 Component model: catch exceptions at component barriers and turn into traps. from the merge queue
alexcrichton added PR #13613 Component model: catch exceptions at component barriers and turn into traps. to the merge queue
:check: alexcrichton merged PR #13613.
alexcrichton removed PR #13613 Component model: catch exceptions at component barriers and turn into traps. from the merge queue
Last updated: Jul 29 2026 at 05:03 UTC