Skip to content

Commit 539bebe

Browse files
authored
Fix missing IS_INVALID_TAGINDEX check in RETHROW handler (#4837)
* Fix RETHROW handler missing IS_INVALID_TAGINDEX check Add validation for exception_tag_index in the RETHROW opcode handler to prevent out-of-bounds access to module->module->tags[] when the tag index is INVALID_TAGINDEX (0xFFFFFFFF). This matches the existing check in the THROW handler. When CATCH_ALL catches a cross-module exception with an unknown tag, it pushes INVALID_TAGINDEX onto the stack. Without this check, a subsequent RETHROW would access tags[0xFFFFFFFF]. * Fix incorrect code section byte counts in exception handling test The hand-crafted WASM binary in load_module_with_exception_handling had an off-by-one in the code section: body size was declared as 7 but the actual body (local count + try/catch_all/end/end) is only 6 bytes. This caused the WASM loader to fail with "unexpected end" when it tried to read past the available bytes. Fix the body size from 7 to 6 and the code section size from 9 to 8.
1 parent fe5276a commit 539bebe

File tree

4 files changed

+220
-5
lines changed

4 files changed

+220
-5
lines changed

core/iwasm/interpreter/wasm_interp_classic.c

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1715,11 +1715,24 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
17151715
exception_tag_index = *((uint32 *)tgtframe_sp);
17161716
tgtframe_sp++;
17171717

1718-
/* get tag type */
1719-
uint8 tag_type_index =
1720-
module->module->tags[exception_tag_index]->type;
1721-
uint32 cell_num_to_copy =
1722-
wasm_types[tag_type_index]->param_cell_num;
1718+
uint32 cell_num_to_copy = 0;
1719+
1720+
if (IS_INVALID_TAGINDEX(exception_tag_index)) {
1721+
/*
1722+
* Cross-module exception with unknown tag.
1723+
* No parameters to copy - just re-throw with
1724+
* the invalid tag index so find_a_catch_handler
1725+
* routes it to CATCH_ALL.
1726+
*/
1727+
cell_num_to_copy = 0;
1728+
}
1729+
else {
1730+
/* get tag type */
1731+
uint8 tag_type_index =
1732+
module->module->tags[exception_tag_index]->type;
1733+
cell_num_to_copy =
1734+
wasm_types[tag_type_index]->param_cell_num;
1735+
}
17231736

17241737
/* move exception parameters (if there are any) onto top
17251738
* of stack */

tests/unit/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ add_subdirectory(gc)
6565
add_subdirectory(tid-allocator)
6666
add_subdirectory(unsupported-features)
6767
add_subdirectory(smart-tests)
68+
add_subdirectory(exception-handling)
6869

6970
if (NOT WAMR_BUILD_TARGET STREQUAL "X86_32")
7071
add_subdirectory(aot-stack-frame)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Copyright (C) 2024 Intel Corporation. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3+
4+
cmake_minimum_required(VERSION 3.14)
5+
6+
project (test-exception-handling)
7+
8+
add_definitions (-DRUN_ON_LINUX)
9+
10+
add_definitions (-Dattr_container_malloc=malloc)
11+
add_definitions (-Dattr_container_free=free)
12+
13+
set (WAMR_BUILD_AOT 0)
14+
set (WAMR_BUILD_INTERP 1)
15+
set (WAMR_BUILD_FAST_INTERP 0)
16+
set (WAMR_BUILD_JIT 0)
17+
set (WAMR_BUILD_LIBC_WASI 0)
18+
set (WAMR_BUILD_APP_FRAMEWORK 0)
19+
set (WAMR_BUILD_EXCE_HANDLING 1)
20+
21+
include (../unit_common.cmake)
22+
23+
include_directories (${CMAKE_CURRENT_SOURCE_DIR})
24+
include_directories (${IWASM_DIR}/interpreter)
25+
26+
file (GLOB_RECURSE source_all ${CMAKE_CURRENT_SOURCE_DIR}/*.cc)
27+
28+
set (UNIT_SOURCE ${source_all})
29+
30+
set (unit_test_sources
31+
${UNIT_SOURCE}
32+
${WAMR_RUNTIME_LIB_SOURCE}
33+
)
34+
35+
# Now simply link against gtest or gtest_main as needed. Eg
36+
add_executable (exception_handling_test ${unit_test_sources})
37+
38+
target_link_libraries (exception_handling_test gtest_main)
39+
40+
gtest_discover_tests(exception_handling_test)
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
* Copyright (C) 2024 Intel Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
*/
5+
6+
#include "gtest/gtest.h"
7+
#include "wasm_runtime_common.h"
8+
#include "bh_platform.h"
9+
10+
/* Pull in the INVALID_TAGINDEX definitions */
11+
#include "wasm_runtime.h"
12+
13+
class ExceptionHandlingTest : public testing::Test
14+
{
15+
protected:
16+
virtual void SetUp()
17+
{
18+
memset(&init_args, 0, sizeof(RuntimeInitArgs));
19+
init_args.mem_alloc_type = Alloc_With_Pool;
20+
init_args.mem_alloc_option.pool.heap_buf = global_heap_buf;
21+
init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf);
22+
ASSERT_EQ(wasm_runtime_full_init(&init_args), true);
23+
}
24+
25+
virtual void TearDown() { wasm_runtime_destroy(); }
26+
27+
public:
28+
char global_heap_buf[512 * 1024];
29+
RuntimeInitArgs init_args;
30+
char error_buf[256];
31+
};
32+
33+
/*
34+
* Test that IS_INVALID_TAGINDEX correctly identifies the invalid tag index
35+
* value (0xFFFFFFFF) used for cross-module exceptions with unknown tags.
36+
*
37+
* The RETHROW handler must check IS_INVALID_TAGINDEX before accessing
38+
* module->module->tags[exception_tag_index]. Without the check,
39+
* exception_tag_index = INVALID_TAGINDEX (0xFFFFFFFF) would cause
40+
* tags[0xFFFFFFFF] -- a massive out-of-bounds read.
41+
*
42+
* The THROW handler at wasm_interp_classic.c properly checks
43+
* IS_INVALID_TAGINDEX, but previously RETHROW did not.
44+
*/
45+
TEST_F(ExceptionHandlingTest, invalid_tagindex_detected)
46+
{
47+
uint32_t tag_index = INVALID_TAGINDEX;
48+
EXPECT_TRUE(IS_INVALID_TAGINDEX(tag_index))
49+
<< "INVALID_TAGINDEX (0xFFFFFFFF) should be detected";
50+
}
51+
52+
TEST_F(ExceptionHandlingTest, valid_tagindex_not_rejected)
53+
{
54+
uint32_t tag_index = 0;
55+
EXPECT_FALSE(IS_INVALID_TAGINDEX(tag_index))
56+
<< "Tag index 0 should not be detected as invalid";
57+
58+
tag_index = 5;
59+
EXPECT_FALSE(IS_INVALID_TAGINDEX(tag_index))
60+
<< "Tag index 5 should not be detected as invalid";
61+
}
62+
63+
/*
64+
* Test that a WASM module with exception handling (try/catch/throw)
65+
* can load and instantiate correctly when exception handling is enabled.
66+
*/
67+
TEST_F(ExceptionHandlingTest, load_module_with_exception_handling)
68+
{
69+
/*
70+
* Minimal WASM module with exception handling:
71+
* - Tag section (id=13): 1 tag, type 0 (no params)
72+
* - Function with try/catch_all/end that does nothing
73+
*/
74+
uint8_t wasm_eh[] = {
75+
0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00,
76+
/* Type section: 1 type, () -> () */
77+
0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
78+
/* Function section: 1 func, type 0 */
79+
0x03, 0x02, 0x01, 0x00,
80+
/* Tag section (id=13): 1 tag, attribute=0, type=0 */
81+
0x0D, 0x03, 0x01, 0x00, 0x00,
82+
/* Export: "f" = func 0 */
83+
0x07, 0x05, 0x01, 0x01, 0x66, 0x00, 0x00,
84+
/* Code section */
85+
0x0A, 0x08, 0x01,
86+
0x06, 0x00, /* body size=6, 0 locals */
87+
0x06, 0x40, /* try (void) */
88+
0x19, /* catch_all */
89+
0x0B, /* end try */
90+
0x0B, /* end func */
91+
};
92+
93+
memset(error_buf, 0, sizeof(error_buf));
94+
wasm_module_t module = wasm_runtime_load(
95+
wasm_eh, sizeof(wasm_eh), error_buf, sizeof(error_buf));
96+
97+
ASSERT_NE(module, nullptr) << "Module load failed: " << error_buf;
98+
99+
wasm_module_inst_t inst = wasm_runtime_instantiate(
100+
module, 8192, 8192, error_buf, sizeof(error_buf));
101+
ASSERT_NE(inst, nullptr) << "Instantiation failed: " << error_buf;
102+
103+
wasm_function_inst_t func =
104+
wasm_runtime_lookup_function(inst, "f");
105+
ASSERT_NE(func, nullptr) << "Function 'f' should be found";
106+
107+
wasm_exec_env_t exec_env =
108+
wasm_runtime_create_exec_env(inst, 8192);
109+
ASSERT_NE(exec_env, nullptr) << "Failed to create exec env";
110+
111+
bool ok = wasm_runtime_call_wasm(exec_env, func, 0, NULL);
112+
ASSERT_TRUE(ok) << "wasm_runtime_call_wasm failed: "
113+
<< wasm_runtime_get_exception(inst);
114+
115+
wasm_runtime_destroy_exec_env(exec_env);
116+
wasm_runtime_deinstantiate(inst);
117+
wasm_runtime_unload(module);
118+
}
119+
120+
/*
121+
* Verify that the RETHROW handler's tag index validation logic matches
122+
* the THROW handler. Both must handle IS_INVALID_TAGINDEX the same way:
123+
* skip the tags[] access and set cell_num_to_copy = 0.
124+
*/
125+
TEST_F(ExceptionHandlingTest, rethrow_handles_invalid_tagindex_like_throw)
126+
{
127+
/* Simulate what both handlers should do with INVALID_TAGINDEX */
128+
uint32_t exception_tag_index = INVALID_TAGINDEX;
129+
uint32_t tag_count = 5;
130+
131+
/* THROW handler logic (reference - always correct): */
132+
uint32_t throw_cell_num = 0;
133+
if (IS_INVALID_TAGINDEX(exception_tag_index)) {
134+
throw_cell_num = 0; /* skip tags[] access */
135+
}
136+
else {
137+
/* would access tags[exception_tag_index] */
138+
throw_cell_num = 42; /* placeholder */
139+
}
140+
141+
/* RETHROW handler logic (after fix - should match THROW): */
142+
uint32_t rethrow_cell_num = 0;
143+
if (IS_INVALID_TAGINDEX(exception_tag_index)) {
144+
rethrow_cell_num = 0; /* skip tags[] access */
145+
}
146+
else {
147+
/* would access tags[exception_tag_index] */
148+
rethrow_cell_num = 42; /* placeholder */
149+
}
150+
151+
EXPECT_EQ(throw_cell_num, rethrow_cell_num)
152+
<< "RETHROW should handle INVALID_TAGINDEX the same as THROW";
153+
154+
EXPECT_EQ(throw_cell_num, 0u)
155+
<< "Both handlers should set cell_num = 0 for INVALID_TAGINDEX";
156+
157+
/* Without the fix, RETHROW would have tried:
158+
* tags[0xFFFFFFFF] => massive OOB read => crash */
159+
EXPECT_TRUE(exception_tag_index >= tag_count)
160+
<< "INVALID_TAGINDEX should be >= any reasonable tag_count";
161+
}

0 commit comments

Comments
 (0)