Fix current-thread leak on concurrent lower error paths#13918
Conversation
When set_thread temporarily switches the store's current thread for guest realloc (or similar), early returns via ? left the wrong thread active. Introduce with_store_thread / restore_thread_after so the previous thread is always restored, preferring the original work error if restore also fails. Apply the helper at every temporary set_thread pair in concurrent lowering, copy, callback delivery, stackful/stackless calls, async return, host lower, and thread start. Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
|
I'd advise against blindly following LLM advice when it comes to the deep internals of the runtime, especially an async runtime. It's intentional that error paths right now do not have the same degree of cleanup as successful paths because errors indicate traps after which a component is entirely locked down and unable to be used again. Are you able to write tests that misbehave using just wasm? Tests for the internal state aren't too interesting since they're not guaranteed to be exposed by the public API. |
|
er, sorry, didn't mean to close |
Thanks for the context. That tracks: these host-side errors (alignment/bounds in
No. The event loop terminates on error before anything could observe the stale state. Closing this; the cleanup asymmetry is consistent with the design. |
Summary
Temporary
set_threadswitches used during concurrent component lowering (so guestrealloc/ callbacks see the correct thread) were not restored on error paths. An early?/bail!afterset_threadleft the store's current thread pointing at the temporary thread, so later concurrent operations could run with the wrong thread context.This adds
with_store_thread/restore_thread_afterthat always restore the previous thread (preferring the original work error if restore also fails), and converts every temporary save/restore pair in this class.Why this is needed
#12736 correctly established that stream/future lowering can call guest
reallocand therefore must set the current thread first. That PR also added restore on the success path. Error paths that return before the matching restore still leave the wrong thread installed.Concrete example in
futures_and_streams::lowerwhenT::MAY_REQUIRE_REALLOCis true:set_thread(caller_thread)installs the caller.linear_store_list_to_memorycan fail with?.set_thread(old_thread)never runs.The same pattern existed for guest↔guest future/stream
copy, event delivery callbacks, stackless/stackful guest calls, async return adapters, host-task result lowering, and explicit thread start.Fix
StoreOpaque::restore_thread_afteralways restores after a workResult.with_store_threadfor the common scoped form.AsStoreOpaqueforStoreContextMutso the helper works at those call sites.set_threadpairs found by greppingcrates/wasmtime/src/runtime/component.Permanent thread transitions (enter/exit sync call, host task create, fiber resume bookkeeping) are unchanged.
Testing
Unit tests in
with_store_thread_tests:got Noneafter installing a host marker thread).Also ran other concurrent unit tests in the crate (
cargo test -p wasmtime --features component-model-async --lib concurrent).Related