Skip to content

[stack-switching] Fix ASan-host interop - #14538

Open
dhil wants to merge 2 commits into
bytecodealliance:mainfrom
dhil:stack-switching-asan-panic
Open

dhil wants to merge 2 commits into
bytecodealliance:mainfrom
dhil:stack-switching-asan-panic

Conversation

@dhil

@dhil dhil commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator

This patch fixes an issue with stack-switching on ASan-enabled builds where resuming continuations from a different host-to-Wasm invocation causes a panic. Because each host invocation creates a fresh initial stack information, it would inadvertently discard the previous stack bounds on a suspended continuation, leaving it without the asan_stack_bottom information.

The fix is to extend the ASan stack-switch handshake with both source and target VMCommonStackInformation pointers:

  1. Before switching, the start hook receives (source_csi, target_csi).
  2. It supplies the target's known bounds to __sanitizer_start_switch_fiber.
  3. It temporarily records the source CSI in a thread-local in-flight slot.
  4. Immediately after switching, the finish hook obtains the previous stack’s bounds from __sanitizer_finish_switch_fiber.
  5. It stores those bounds in the recorded source CSI and clears the in-flight slot.

Resolves #14508

@dhil
dhil requested review from a team as code owners October 5, 2026 14:55
@dhil
dhil requested review from alexcrichton and removed request for a team October 5, 2026 14:55
@dhil
dhil force-pushed the stack-switching-asan-panic branch from a16f730 to 9a3a120 Compare October 5, 2026 14:55
(bottom.as_ptr(), csi.asan_stack_size)
}

#[cfg_attr(asan, sanitize(address = "off"))]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come these annotations are necessary? These aren't present in the craters/fiber code so I'm mostly just curious

@dhil dhil Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have put them conservatively here. Based on my understanding the wrapper functions invoking the ASan primitives shouldn't themselves be instrumented as it can lead to false-positives or crashes under certain ASan configurations, e.g. I got it from reading this issue google/sanitizers#1760. Maybe it is being overly cautious?

std::thread_local! {
/// The stack whose bounds ASan will report at the next matching
/// `__sanitizer_finish_switch_fiber` call.
static PENDING_SOURCE_CSI: Cell<*mut VMCommonStackInformation> =

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm personally always a bit loathe to introduce more global/thread-local state -- would it be possibel to use some preexisting pointer storage for this? I'm not fully following what this is used for, but given the pointers and/or VMCommonStackInformation it naively seems like one of those can be used, but I'm also likely missing something

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The purpose of this global is to serve as a sort of mailbox for transferring bookkeeping data between stack switches. It is needed because ASan reports the old stack's bounds only after execution has moved to the destination stack. I don't think the API provides any way to transfer the necessary source metadata across that switch. I think it is important to note that this slot holds only the state during the switch handshake, i.e. its lifetime is brief and not dependent on the lifetime of the suspended continuation.

Regarding using a global: I tend to agree, but in this case I think a private global is the right thing, because it is used only in an esoteric debug build. So in a certain sense everything is nicely localised here. An alternative would be to stash it on the VMCommonStackInformation. I don't like this because it affects the continuation layout for production builds (relatedly I do plan to look into making the layout leaner).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will try to experiment with adding it to VMCommonStackInformation, seeing that there are two other ASan-related things there already I figured it is better to bundle them together such that I have all the bits in one place then I later get to optimise the representation.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've implemented the change now. Note I had to slightly reorganise the fields of VMCommonStackInformation to ensure that revision remains at an 8-aligned offset.

@github-actions github-actions Bot added the wasmtime:api Related to the API of the `wasmtime` crate itself label Oct 5, 2026
This patch fixes an issue with stack-switching on ASan-enabled builds
where resuming continuations from a different host-to-Wasm invocation
causes a panic. Because each host invocation creates a fresh initial
stack information, it would inadvertently discard the previous stack
bounds on a suspended continuation, leaving it without the
`asan_stack_bottom` information.

The fix is to extend the ASan stack-switch handshake with both source
and target `VMCommonStackInformation` pointers:

1. Before switching, the start hook receives `(source_csi,
target_csi)`.
2. It supplies the target's known bounds to
`__sanitizer_start_switch_fiber`.
3. It temporarily records the source CSI in a thread-local in-flight
slot.
4. Immediately after switching, the finish hook obtains the previous
stack’s bounds from `__sanitizer_finish_switch_fiber`.
5. It stores those bounds in the recorded source CSI and clears the
in-flight slot.

prtest:full
@dhil
dhil force-pushed the stack-switching-asan-panic branch from 9a3a120 to acd9edb Compare October 7, 2026 10:47
structure.

This commit also removes the `PENDING_CSI` global, which was used to
communicate bookkeeping information between ASan stack switches.

This change does inflate the `VMCommonStackInformation` and by
extension `VMContRef` objects. However, I think this space can
subsequently be reclaimed selecting a zero-size no-op implementation
of the structure in non-ASan builds (requires changing direct field
accessor to be indirect via function calls). I will look into this in
a future PR.

prtest:full
@dhil
dhil force-pushed the stack-switching-asan-panic branch from acd9edb to 8ece790 Compare October 7, 2026 11:17

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

wasmtime:api Related to the API of the `wasmtime` crate itself

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stack-switching + asan panic

2 participants