|
| 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, 0x09, 0x01, |
| 86 | + 0x07, 0x00, /* body size=7, 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 | + if (!module) { |
| 98 | + GTEST_SKIP() << "Module load failed: " << error_buf; |
| 99 | + } |
| 100 | + |
| 101 | + wasm_module_inst_t inst = wasm_runtime_instantiate( |
| 102 | + module, 8192, 8192, error_buf, sizeof(error_buf)); |
| 103 | + ASSERT_NE(inst, nullptr) << "Instantiation failed: " << error_buf; |
| 104 | + |
| 105 | + wasm_function_inst_t func = |
| 106 | + wasm_runtime_lookup_function(inst, "f"); |
| 107 | + ASSERT_NE(func, nullptr) << "Function 'f' should be found"; |
| 108 | + |
| 109 | + wasm_exec_env_t exec_env = |
| 110 | + wasm_runtime_create_exec_env(inst, 8192); |
| 111 | + ASSERT_NE(exec_env, nullptr) << "Failed to create exec env"; |
| 112 | + |
| 113 | + bool ok = wasm_runtime_call_wasm(exec_env, func, 0, NULL); |
| 114 | + if (!ok) { |
| 115 | + const char *exception = wasm_runtime_get_exception(inst); |
| 116 | + EXPECT_NE(exception, nullptr) |
| 117 | + << "Call failed but no exception was set"; |
| 118 | + } |
| 119 | + |
| 120 | + wasm_runtime_destroy_exec_env(exec_env); |
| 121 | + wasm_runtime_deinstantiate(inst); |
| 122 | + wasm_runtime_unload(module); |
| 123 | +} |
| 124 | + |
| 125 | +/* |
| 126 | + * Verify that the RETHROW handler's tag index validation logic matches |
| 127 | + * the THROW handler. Both must handle IS_INVALID_TAGINDEX the same way: |
| 128 | + * skip the tags[] access and set cell_num_to_copy = 0. |
| 129 | + */ |
| 130 | +TEST_F(ExceptionHandlingTest, rethrow_handles_invalid_tagindex_like_throw) |
| 131 | +{ |
| 132 | + /* Simulate what both handlers should do with INVALID_TAGINDEX */ |
| 133 | + uint32_t exception_tag_index = INVALID_TAGINDEX; |
| 134 | + uint32_t tag_count = 5; |
| 135 | + |
| 136 | + /* THROW handler logic (reference - always correct): */ |
| 137 | + uint32_t throw_cell_num = 0; |
| 138 | + if (IS_INVALID_TAGINDEX(exception_tag_index)) { |
| 139 | + throw_cell_num = 0; /* skip tags[] access */ |
| 140 | + } |
| 141 | + else { |
| 142 | + /* would access tags[exception_tag_index] */ |
| 143 | + throw_cell_num = 42; /* placeholder */ |
| 144 | + } |
| 145 | + |
| 146 | + /* RETHROW handler logic (after fix - should match THROW): */ |
| 147 | + uint32_t rethrow_cell_num = 0; |
| 148 | + if (IS_INVALID_TAGINDEX(exception_tag_index)) { |
| 149 | + rethrow_cell_num = 0; /* skip tags[] access */ |
| 150 | + } |
| 151 | + else { |
| 152 | + /* would access tags[exception_tag_index] */ |
| 153 | + rethrow_cell_num = 42; /* placeholder */ |
| 154 | + } |
| 155 | + |
| 156 | + EXPECT_EQ(throw_cell_num, rethrow_cell_num) |
| 157 | + << "RETHROW should handle INVALID_TAGINDEX the same as THROW"; |
| 158 | + |
| 159 | + EXPECT_EQ(throw_cell_num, 0u) |
| 160 | + << "Both handlers should set cell_num = 0 for INVALID_TAGINDEX"; |
| 161 | + |
| 162 | + /* Without the fix, RETHROW would have tried: |
| 163 | + * tags[0xFFFFFFFF] => massive OOB read => crash */ |
| 164 | + EXPECT_TRUE(exception_tag_index >= tag_count) |
| 165 | + << "INVALID_TAGINDEX should be >= any reasonable tag_count"; |
| 166 | +} |
0 commit comments