Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions core/iwasm/interpreter/wasm_interp_classic.c
Original file line number Diff line number Diff line change
Expand Up @@ -1715,11 +1715,24 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
exception_tag_index = *((uint32 *)tgtframe_sp);
tgtframe_sp++;

/* get tag type */
uint8 tag_type_index =
module->module->tags[exception_tag_index]->type;
uint32 cell_num_to_copy =
wasm_types[tag_type_index]->param_cell_num;
uint32 cell_num_to_copy = 0;

if (IS_INVALID_TAGINDEX(exception_tag_index)) {
/*
* Cross-module exception with unknown tag.
* No parameters to copy - just re-throw with
* the invalid tag index so find_a_catch_handler
* routes it to CATCH_ALL.
*/
cell_num_to_copy = 0;
}
else {
/* get tag type */
uint8 tag_type_index =
module->module->tags[exception_tag_index]->type;
cell_num_to_copy =
wasm_types[tag_type_index]->param_cell_num;
}

/* move exception parameters (if there are any) onto top
* of stack */
Expand Down
1 change: 1 addition & 0 deletions tests/unit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ add_subdirectory(gc)
add_subdirectory(tid-allocator)
add_subdirectory(unsupported-features)
add_subdirectory(smart-tests)
add_subdirectory(exception-handling)

if (NOT WAMR_BUILD_TARGET STREQUAL "X86_32")
add_subdirectory(aot-stack-frame)
Expand Down
40 changes: 40 additions & 0 deletions tests/unit/exception-handling/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright (C) 2024 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

cmake_minimum_required(VERSION 3.14)

project (test-exception-handling)

add_definitions (-DRUN_ON_LINUX)

add_definitions (-Dattr_container_malloc=malloc)
add_definitions (-Dattr_container_free=free)

set (WAMR_BUILD_AOT 0)
set (WAMR_BUILD_INTERP 1)
set (WAMR_BUILD_FAST_INTERP 0)
set (WAMR_BUILD_JIT 0)
set (WAMR_BUILD_LIBC_WASI 0)
set (WAMR_BUILD_APP_FRAMEWORK 0)
set (WAMR_BUILD_EXCE_HANDLING 1)

include (../unit_common.cmake)

include_directories (${CMAKE_CURRENT_SOURCE_DIR})
include_directories (${IWASM_DIR}/interpreter)

file (GLOB_RECURSE source_all ${CMAKE_CURRENT_SOURCE_DIR}/*.cc)

set (UNIT_SOURCE ${source_all})

set (unit_test_sources
${UNIT_SOURCE}
${WAMR_RUNTIME_LIB_SOURCE}
)

# Now simply link against gtest or gtest_main as needed. Eg
add_executable (exception_handling_test ${unit_test_sources})

target_link_libraries (exception_handling_test gtest_main)

gtest_discover_tests(exception_handling_test)
161 changes: 161 additions & 0 deletions tests/unit/exception-handling/exception_handling_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* Copyright (C) 2024 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/

#include "gtest/gtest.h"
#include "wasm_runtime_common.h"
#include "bh_platform.h"

/* Pull in the INVALID_TAGINDEX definitions */
#include "wasm_runtime.h"

class ExceptionHandlingTest : public testing::Test
{
protected:
virtual void SetUp()
{
memset(&init_args, 0, sizeof(RuntimeInitArgs));
init_args.mem_alloc_type = Alloc_With_Pool;
init_args.mem_alloc_option.pool.heap_buf = global_heap_buf;
init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf);
ASSERT_EQ(wasm_runtime_full_init(&init_args), true);
}

virtual void TearDown() { wasm_runtime_destroy(); }

public:
char global_heap_buf[512 * 1024];
RuntimeInitArgs init_args;
char error_buf[256];
};

/*
* Test that IS_INVALID_TAGINDEX correctly identifies the invalid tag index
* value (0xFFFFFFFF) used for cross-module exceptions with unknown tags.
*
* The RETHROW handler must check IS_INVALID_TAGINDEX before accessing
* module->module->tags[exception_tag_index]. Without the check,
* exception_tag_index = INVALID_TAGINDEX (0xFFFFFFFF) would cause
* tags[0xFFFFFFFF] -- a massive out-of-bounds read.
*
* The THROW handler at wasm_interp_classic.c properly checks
* IS_INVALID_TAGINDEX, but previously RETHROW did not.
*/
TEST_F(ExceptionHandlingTest, invalid_tagindex_detected)
{
uint32_t tag_index = INVALID_TAGINDEX;
EXPECT_TRUE(IS_INVALID_TAGINDEX(tag_index))
<< "INVALID_TAGINDEX (0xFFFFFFFF) should be detected";
}

TEST_F(ExceptionHandlingTest, valid_tagindex_not_rejected)
{
uint32_t tag_index = 0;
EXPECT_FALSE(IS_INVALID_TAGINDEX(tag_index))
<< "Tag index 0 should not be detected as invalid";

tag_index = 5;
EXPECT_FALSE(IS_INVALID_TAGINDEX(tag_index))
<< "Tag index 5 should not be detected as invalid";
}

/*
* Test that a WASM module with exception handling (try/catch/throw)
* can load and instantiate correctly when exception handling is enabled.
*/
TEST_F(ExceptionHandlingTest, load_module_with_exception_handling)
{
/*
* Minimal WASM module with exception handling:
* - Tag section (id=13): 1 tag, type 0 (no params)
* - Function with try/catch_all/end that does nothing
*/
uint8_t wasm_eh[] = {
0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00,
/* Type section: 1 type, () -> () */
0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
/* Function section: 1 func, type 0 */
0x03, 0x02, 0x01, 0x00,
/* Tag section (id=13): 1 tag, attribute=0, type=0 */
0x0D, 0x03, 0x01, 0x00, 0x00,
/* Export: "f" = func 0 */
0x07, 0x05, 0x01, 0x01, 0x66, 0x00, 0x00,
/* Code section */
0x0A, 0x08, 0x01,
0x06, 0x00, /* body size=6, 0 locals */
0x06, 0x40, /* try (void) */
0x19, /* catch_all */
0x0B, /* end try */
0x0B, /* end func */
};

memset(error_buf, 0, sizeof(error_buf));
wasm_module_t module = wasm_runtime_load(
wasm_eh, sizeof(wasm_eh), error_buf, sizeof(error_buf));

ASSERT_NE(module, nullptr) << "Module load failed: " << error_buf;

wasm_module_inst_t inst = wasm_runtime_instantiate(
module, 8192, 8192, error_buf, sizeof(error_buf));
ASSERT_NE(inst, nullptr) << "Instantiation failed: " << error_buf;

wasm_function_inst_t func =
wasm_runtime_lookup_function(inst, "f");
ASSERT_NE(func, nullptr) << "Function 'f' should be found";

wasm_exec_env_t exec_env =
wasm_runtime_create_exec_env(inst, 8192);
ASSERT_NE(exec_env, nullptr) << "Failed to create exec env";

bool ok = wasm_runtime_call_wasm(exec_env, func, 0, NULL);
ASSERT_TRUE(ok) << "wasm_runtime_call_wasm failed: "
<< wasm_runtime_get_exception(inst);

wasm_runtime_destroy_exec_env(exec_env);
wasm_runtime_deinstantiate(inst);
wasm_runtime_unload(module);
}

/*
* Verify that the RETHROW handler's tag index validation logic matches
* the THROW handler. Both must handle IS_INVALID_TAGINDEX the same way:
* skip the tags[] access and set cell_num_to_copy = 0.
*/
TEST_F(ExceptionHandlingTest, rethrow_handles_invalid_tagindex_like_throw)
{
/* Simulate what both handlers should do with INVALID_TAGINDEX */
uint32_t exception_tag_index = INVALID_TAGINDEX;
uint32_t tag_count = 5;

/* THROW handler logic (reference - always correct): */
uint32_t throw_cell_num = 0;
if (IS_INVALID_TAGINDEX(exception_tag_index)) {
throw_cell_num = 0; /* skip tags[] access */
}
else {
/* would access tags[exception_tag_index] */
throw_cell_num = 42; /* placeholder */
}

/* RETHROW handler logic (after fix - should match THROW): */
uint32_t rethrow_cell_num = 0;
if (IS_INVALID_TAGINDEX(exception_tag_index)) {
rethrow_cell_num = 0; /* skip tags[] access */
}
else {
/* would access tags[exception_tag_index] */
rethrow_cell_num = 42; /* placeholder */
}

EXPECT_EQ(throw_cell_num, rethrow_cell_num)
<< "RETHROW should handle INVALID_TAGINDEX the same as THROW";

EXPECT_EQ(throw_cell_num, 0u)
<< "Both handlers should set cell_num = 0 for INVALID_TAGINDEX";

/* Without the fix, RETHROW would have tried:
* tags[0xFFFFFFFF] => massive OOB read => crash */
EXPECT_TRUE(exception_tag_index >= tag_count)
<< "INVALID_TAGINDEX should be >= any reasonable tag_count";
}
Loading