Stream: git-wasmtime

Topic: wasmtime / issue #14965 Read-only access to numeric globa...


view this post on Zulip Wasmtime GitHub notifications bot (Oct 23 2026 at 16:35):

smarcd opened issue #14965:

#14386 narrowed epoch-deadline and call-hook callbacks to StoreHookState, which implements AsContext and gives &mut T. We agree with the rationale: mutating wasm-visible state between instructions is wrong.

That change leaves one read-only use case with no API: reading the current value of a mutable numeric global from inside the callback.

Global::get takes impl AsContextMut. As far as I can tell that is because the Ref arms clone GC refs (clone_gc_ref, ExternRef::from_cloned_gc_ref) and need AutoAssertNoGc. For i32/i64/f32/f64 (and v128) it is a plain load from the VMGlobalDefinition and mutates nothing. So there is no way to read a numeric global with only an AsContext.

Our use case: an embedder that snapshots a suspended guest thread reads the guest's mutable globals (such as the shadow stack pointer) at the moment an epoch tick parks the thread, while the wasm frames are still live on the stack. The thread is paused at a safe point; nothing is mutated, and we do not call back into wasm.

Proposal: a getter that takes impl AsContext and returns Option<Val>/Result<Val> for non-reference global types (returning None/Err for Ref types), e.g. Global::get_numeric. It would be safe to expose from StoreHookState for the same reason Memory::data(&store) already is.

If you would rather not add API surface, is there an intended way for an embedder to observe global values from an epoch callback now? The DebugHandler route (DebugEvent::EpochYield) seems to require guest_debug and async, which is heavier than we want for this.

Happy to send a PR if this direction is acceptable.

view this post on Zulip Wasmtime GitHub notifications bot (Oct 23 2026 at 20:23):

alexcrichton commented on issue #14965:

Thanks for the report! This was a known possible downside of #14386 and there's no current replacement for the functionality.

One of the downsides of an accessor like this is that it's so specialized to just numeric globals and doesn't handle the GC proposal of wasm. Originally Global::get used to take AsContext but with GC that's where it changed to AsContextMut because rooting/frobbing needs to happen in that situation. I realize though that's exactly the intention here, but the second downside is that this is duplicating API/implementation surface area for this.

That being said I don't really see a way around this, and I think it's a reasonable ask to be able to read globals with just AsContext. We'll eventually need to extend that to tables too most likely, but that can probably wait until it's needed.

Bikeshedding-wise I don't have any great idea other than get_numeric, so that seems reasonable to me. If you'd like to send a PR we can review.

view this post on Zulip Wasmtime GitHub notifications bot (Oct 23 2026 at 20:23):

alexcrichton added the enhancement label to Issue #14965.

view this post on Zulip Wasmtime GitHub notifications bot (Oct 23 2026 at 20:23):

alexcrichton added the wasmtime:api label to Issue #14965.

view this post on Zulip Wasmtime GitHub notifications bot (Oct 26 2026 at 21:35):

pchickey closed issue #14965:

#14386 narrowed epoch-deadline and call-hook callbacks to StoreHookState, which implements AsContext and gives &mut T. We agree with the rationale: mutating wasm-visible state between instructions is wrong.

That change leaves one read-only use case with no API: reading the current value of a mutable numeric global from inside the callback.

Global::get takes impl AsContextMut. As far as I can tell that is because the Ref arms clone GC refs (clone_gc_ref, ExternRef::from_cloned_gc_ref) and need AutoAssertNoGc. For i32/i64/f32/f64 (and v128) it is a plain load from the VMGlobalDefinition and mutates nothing. So there is no way to read a numeric global with only an AsContext.

Our use case: an embedder that snapshots a suspended guest thread reads the guest's mutable globals (such as the shadow stack pointer) at the moment an epoch tick parks the thread, while the wasm frames are still live on the stack. The thread is paused at a safe point; nothing is mutated, and we do not call back into wasm.

Proposal: a getter that takes impl AsContext and returns Option<Val>/Result<Val> for non-reference global types (returning None/Err for Ref types), e.g. Global::get_numeric. It would be safe to expose from StoreHookState for the same reason Memory::data(&store) already is.

If you would rather not add API surface, is there an intended way for an embedder to observe global values from an epoch callback now? The DebugHandler route (DebugEvent::EpochYield) seems to require guest_debug and async, which is heavier than we want for this.

Happy to send a PR if this direction is acceptable.


Last updated: Oct 31 2026 at 23:03 UTC