Skip to content

Commit 5664589

Browse files
authored
fix: clear exec_env_tls when destroying exec_env (#4774)
* fix: clear exec_env_tls when destroying exec_env When an exec_env is destroyed, check if it matches the current thread's exec_env_tls and clear it to avoid dangling pointer issues. Without this fix, in daemon-style execution where the same thread runs multiple WASM modules sequentially (like Cloudflare Workers), the exec_env_tls can point to freed memory after an exec_env is destroyed, causing crashes on subsequent executions when the signal handler tries to access it. This is critical for AOT mode with hardware bounds checking enabled, where signal handlers rely on exec_env_tls to handle SIGSEGV properly. * test(exec_env): add reproducer for exec_env_tls dangling pointer bug Add test case that reproduces the bug where exec_env_tls is not cleared on early return paths in invoke_native_with_hw_bound_check. The test triggers native stack overflow check failure, which causes wasm_runtime_call_wasm to return early after setting exec_env_tls but without clearing it. This leaves exec_env_tls pointing to a destroyed exec_env, causing subsequent calls to fail with "invalid exec env". Test confirms the fix in wasm_exec_env_destroy correctly clears exec_env_tls when destroying the exec_env it points to. * fix(runtime): clear exec_env_tls on early return from stack overflow check Move the fix to clear exec_env_tls at the source - in the early return path of invoke_native_with_hw_bound_check when native stack overflow check fails.
1 parent d4034f1 commit 5664589

File tree

3 files changed

+92
-0
lines changed

3 files changed

+92
-0
lines changed

core/iwasm/aot/aot_runtime.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2485,6 +2485,7 @@ invoke_native_with_hw_bound_check(WASMExecEnv *exec_env, void *func_ptr,
24852485
native stack to run the following codes before actually calling
24862486
the aot function in invokeNative function. */
24872487
if (!wasm_runtime_detect_native_stack_overflow(exec_env)) {
2488+
wasm_runtime_set_exec_env_tls(NULL);
24882489
return false;
24892490
}
24902491

core/iwasm/interpreter/wasm_runtime.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3618,6 +3618,7 @@ call_wasm_with_hw_bound_check(WASMModuleInstance *module_inst,
36183618
native stack to run the following codes before actually calling
36193619
the aot function in invokeNative function. */
36203620
if (!wasm_runtime_detect_native_stack_overflow(exec_env)) {
3621+
wasm_runtime_set_exec_env_tls(NULL);
36213622
return;
36223623
}
36233624

tests/unit/runtime-common/wasm_exec_env_test.cc

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "gtest/gtest.h"
88

99
#include "wasm_exec_env.h"
10+
#include "wasm_runtime_common.h"
1011

1112
class wasm_exec_env_test_suite : public testing::Test
1213
{
@@ -43,3 +44,92 @@ TEST_F(wasm_exec_env_test_suite, wasm_exec_env_pop_jmpbuf)
4344
exec_env.jmpbuf_stack_top = nullptr;
4445
EXPECT_EQ(nullptr, wasm_exec_env_pop_jmpbuf(&exec_env));
4546
}
47+
48+
/*
49+
* Test: exec_env_tls is cleared on early return from native stack overflow
50+
*
51+
* This test verifies that when wasm_runtime_call_wasm fails early due to
52+
* native stack overflow check, exec_env_tls is properly cleared. Without
53+
* this fix, subsequent calls with a different exec_env would fail with
54+
* "invalid exec env" error.
55+
*
56+
* Bug scenario:
57+
* 1. Call WASM with exec_env_A, TLS set to exec_env_A
58+
* 2. Native stack overflow check fails, early return
59+
* 3. TLS still points to exec_env_A (BUG: not cleared)
60+
* 4. Destroy exec_env_A
61+
* 5. Create exec_env_B, call WASM
62+
* 6. Fails with "invalid exec env" because TLS != exec_env_B
63+
*/
64+
#ifdef OS_ENABLE_HW_BOUND_CHECK
65+
/* Minimal WASM module: (module (func (export "test") (result i32) i32.const
66+
* 42)) */
67+
static uint8_t test_wasm[] = { 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
68+
0x01, 0x05, 0x01, 0x60, 0x00, 0x01, 0x7f, 0x03,
69+
0x02, 0x01, 0x00, 0x07, 0x08, 0x01, 0x04, 0x74,
70+
0x65, 0x73, 0x74, 0x00, 0x00, 0x0a, 0x06, 0x01,
71+
0x04, 0x00, 0x41, 0x2a, 0x0b };
72+
73+
TEST_F(wasm_exec_env_test_suite, exec_env_tls_cleared_on_stack_overflow)
74+
{
75+
WAMRRuntimeRAII<512 * 1024> runtime;
76+
77+
/* Load and instantiate module */
78+
wasm_module_t module =
79+
wasm_runtime_load(test_wasm, sizeof(test_wasm), nullptr, 0);
80+
ASSERT_NE(module, nullptr);
81+
82+
wasm_module_inst_t inst_a =
83+
wasm_runtime_instantiate(module, 8192, 8192, nullptr, 0);
84+
ASSERT_NE(inst_a, nullptr);
85+
86+
wasm_exec_env_t exec_env_a =
87+
wasm_runtime_create_exec_env(inst_a, 8192);
88+
ASSERT_NE(exec_env_a, nullptr);
89+
90+
/* Set native stack boundary high to trigger overflow check failure */
91+
uint8_t stack_var;
92+
uint8_t *high_boundary = &stack_var + 0x100000;
93+
wasm_runtime_set_native_stack_boundary(exec_env_a, high_boundary);
94+
95+
/* Call should fail with native stack overflow */
96+
wasm_function_inst_t func =
97+
wasm_runtime_lookup_function(inst_a, "test");
98+
ASSERT_NE(func, nullptr);
99+
100+
uint32_t argv[1] = { 0 };
101+
bool result = wasm_runtime_call_wasm(exec_env_a, func, 0, argv);
102+
EXPECT_FALSE(result);
103+
104+
/* Verify TLS is cleared after failed call (this is the fix) */
105+
WASMExecEnv *tls = wasm_runtime_get_exec_env_tls();
106+
EXPECT_EQ(tls, nullptr);
107+
108+
/* Clean up first instance */
109+
wasm_runtime_destroy_exec_env(exec_env_a);
110+
wasm_runtime_deinstantiate(inst_a);
111+
112+
/* Create second instance and exec_env */
113+
wasm_module_inst_t inst_b =
114+
wasm_runtime_instantiate(module, 8192, 8192, nullptr, 0);
115+
ASSERT_NE(inst_b, nullptr);
116+
117+
wasm_exec_env_t exec_env_b =
118+
wasm_runtime_create_exec_env(inst_b, 8192);
119+
ASSERT_NE(exec_env_b, nullptr);
120+
121+
/* This call should succeed (would fail without the fix) */
122+
func = wasm_runtime_lookup_function(inst_b, "test");
123+
ASSERT_NE(func, nullptr);
124+
125+
argv[0] = 0;
126+
result = wasm_runtime_call_wasm(exec_env_b, func, 0, argv);
127+
EXPECT_TRUE(result);
128+
EXPECT_EQ(argv[0], 42u);
129+
130+
/* Clean up */
131+
wasm_runtime_destroy_exec_env(exec_env_b);
132+
wasm_runtime_deinstantiate(inst_b);
133+
wasm_runtime_unload(module);
134+
}
135+
#endif /* OS_ENABLE_HW_BOUND_CHECK */

0 commit comments

Comments
 (0)