From 68fc2323e0fa881061acbc0592e100e93cb703db Mon Sep 17 00:00:00 2001 From: Aizal Khan Date: Thu, 16 Jul 2026 19:44:25 +0530 Subject: [PATCH 1/2] return after rethrow in c-writer instead of break --- src/c-writer.cc | 4 +++- src/test-c-writer.cc | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/c-writer.cc b/src/c-writer.cc index 923397bf98..74252fb599 100644 --- a/src/c-writer.cc +++ b/src/c-writer.cc @@ -4187,7 +4187,9 @@ void CWriter::Write(const ExprList& exprs) { Write("wasm_rt_load_exception(", ex, "_tag, ", ex, "_size, ", ex, ");", Newline()); WriteThrow(); - } break; + // Stop processing this ExprList, since the following are unreachable. + return; + } case ExprType::Try: { const TryExpr& tryexpr = *cast(&expr); diff --git a/src/test-c-writer.cc b/src/test-c-writer.cc index d51631856e..62d51aa650 100644 --- a/src/test-c-writer.cc +++ b/src/test-c-writer.cc @@ -45,13 +45,15 @@ const uint8_t s_float_globals[] = { 0x03, 0x00, 0x05, 0x67, 0x5f, 0x66, 0x33, 0x32, 0x03, 0x01, }; -std::string WriteCSource(const uint8_t* data, size_t size) { +std::string WriteCSource(const uint8_t* data, + size_t size, + const Features& features = Features{}) { Errors errors; Module module; ReadBinaryOptions read_options; + read_options.features = features; EXPECT_EQ(Result::Ok, ReadBinaryIr("test", data, size, read_options, &errors, &module)); - Features features; ValidateOptions validate_options(features); EXPECT_EQ(Result::Ok, ValidateModule(&module, &errors, validate_options)); EXPECT_EQ(Result::Ok, GenerateNames(&module)); @@ -101,3 +103,33 @@ TEST(CWriter, FloatConstLocaleIndependent) { EXPECT_EQ(source.find("1,5"), std::string::npos); EXPECT_EQ(source.find("0,5"), std::string::npos); } + +// rethrow transfers control unconditionally, so the validator marks the rest +// of the block unreachable and accepts instructions there against a +// polymorphic stack. The generator must stop emitting the block at such a +// terminator; walking into the unreachable tail pops operands that do not +// exist and reads past the end of the type stack (StackVar underflow). +// Translating this valid module must not fault. +TEST(CWriter, RethrowFollowedByUnreachableCode) { + // (module + // (memory 1) + // (func try nop catch_all rethrow 0 i32.store end)) + const uint8_t data[] = { + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, // magic + version + 0x01, 0x04, 0x01, 0x60, 0x00, 0x00, // type: () -> () + 0x03, 0x02, 0x01, 0x00, // func: type 0 + 0x05, 0x03, 0x01, 0x00, 0x01, // memory: min 1 + 0x0a, 0x0e, 0x01, 0x0c, 0x00, // code: 1 body, size 12 + 0x06, 0x40, // try (void) + 0x01, // nop + 0x19, // catch_all + 0x09, 0x00, // rethrow 0 + 0x36, 0x02, 0x00, // i32.store (unreachable) + 0x0b, // end try + 0x0b, // end func + }; + + Features features; + features.enable_exceptions(); + WriteCSource(data, sizeof(data), features); +} From 9a9c8a935347a5bf24b2d0e943bd1316da871001 Mon Sep 17 00:00:00 2001 From: Aizal Khan Date: Fri, 17 Jul 2026 15:47:10 +0530 Subject: [PATCH 2/2] Move rethrow underflow regression test into test/wasm2c --- src/test-c-writer.cc | 36 ++--------------------- test/wasm2c/unreachable-after-rethrow.txt | 15 ++++++++++ 2 files changed, 17 insertions(+), 34 deletions(-) create mode 100644 test/wasm2c/unreachable-after-rethrow.txt diff --git a/src/test-c-writer.cc b/src/test-c-writer.cc index 62d51aa650..d51631856e 100644 --- a/src/test-c-writer.cc +++ b/src/test-c-writer.cc @@ -45,15 +45,13 @@ const uint8_t s_float_globals[] = { 0x03, 0x00, 0x05, 0x67, 0x5f, 0x66, 0x33, 0x32, 0x03, 0x01, }; -std::string WriteCSource(const uint8_t* data, - size_t size, - const Features& features = Features{}) { +std::string WriteCSource(const uint8_t* data, size_t size) { Errors errors; Module module; ReadBinaryOptions read_options; - read_options.features = features; EXPECT_EQ(Result::Ok, ReadBinaryIr("test", data, size, read_options, &errors, &module)); + Features features; ValidateOptions validate_options(features); EXPECT_EQ(Result::Ok, ValidateModule(&module, &errors, validate_options)); EXPECT_EQ(Result::Ok, GenerateNames(&module)); @@ -103,33 +101,3 @@ TEST(CWriter, FloatConstLocaleIndependent) { EXPECT_EQ(source.find("1,5"), std::string::npos); EXPECT_EQ(source.find("0,5"), std::string::npos); } - -// rethrow transfers control unconditionally, so the validator marks the rest -// of the block unreachable and accepts instructions there against a -// polymorphic stack. The generator must stop emitting the block at such a -// terminator; walking into the unreachable tail pops operands that do not -// exist and reads past the end of the type stack (StackVar underflow). -// Translating this valid module must not fault. -TEST(CWriter, RethrowFollowedByUnreachableCode) { - // (module - // (memory 1) - // (func try nop catch_all rethrow 0 i32.store end)) - const uint8_t data[] = { - 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, // magic + version - 0x01, 0x04, 0x01, 0x60, 0x00, 0x00, // type: () -> () - 0x03, 0x02, 0x01, 0x00, // func: type 0 - 0x05, 0x03, 0x01, 0x00, 0x01, // memory: min 1 - 0x0a, 0x0e, 0x01, 0x0c, 0x00, // code: 1 body, size 12 - 0x06, 0x40, // try (void) - 0x01, // nop - 0x19, // catch_all - 0x09, 0x00, // rethrow 0 - 0x36, 0x02, 0x00, // i32.store (unreachable) - 0x0b, // end try - 0x0b, // end func - }; - - Features features; - features.enable_exceptions(); - WriteCSource(data, sizeof(data), features); -} diff --git a/test/wasm2c/unreachable-after-rethrow.txt b/test/wasm2c/unreachable-after-rethrow.txt new file mode 100644 index 0000000000..e4bffbfcfd --- /dev/null +++ b/test/wasm2c/unreachable-after-rethrow.txt @@ -0,0 +1,15 @@ +;;; TOOL: run-spec-wasm2c +;;; ARGS*: --enable-exceptions +(module + (memory 1) + (func (export "test") + try + nop + catch_all + rethrow 0 + i32.store + end)) +(assert_return (invoke "test")) +(;; STDOUT ;;; +1/1 tests passed. +;;; STDOUT ;;)