From 147464de3f39fae2498ee1f50759af318d9f9178 Mon Sep 17 00:00:00 2001 From: Aizal Khan Date: Fri, 14 Aug 2026 21:45:53 +0530 Subject: [PATCH 1/2] truncate resolve lists when recovering from a failed instruction --- include/wabt/wast-parser.h | 3 ++ src/wast-parser.cc | 38 +++++++++++++++---- .../parse/bad-ref-type-in-recovered-instr.txt | 21 ++++++++++ 3 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 test/parse/bad-ref-type-in-recovered-instr.txt diff --git a/include/wabt/wast-parser.h b/include/wabt/wast-parser.h index e071c5c8d..8f605568d 100644 --- a/include/wabt/wast-parser.h +++ b/include/wabt/wast-parser.h @@ -207,6 +207,9 @@ class WastParser { Result ParseModuleFieldList(Module*); Result ParseModuleField(Module*); Result ParseModuleFieldImpl(Module*); + void TruncateResolveLists(size_t ref_types_size, + size_t type_vectors_size, + size_t funcs_size); Result ParseDataModuleField(Module*); Result ParseElemModuleField(Module*); Result ParseTagModuleField(Module*); diff --git a/src/wast-parser.cc b/src/wast-parser.cc index 4fcfd4dbd..fd5865f06 100644 --- a/src/wast-parser.cc +++ b/src/wast-parser.cc @@ -1506,18 +1506,23 @@ Result WastParser::ParseModuleField(Module* module) { Result result = ParseModuleFieldImpl(module); if (Failed(result)) { - resolve_ref_types_.erase(resolve_ref_types_.begin() + ref_types_size, - resolve_ref_types_.end()); - resolve_type_vectors_.erase( - resolve_type_vectors_.begin() + type_vectors_size, - resolve_type_vectors_.end()); - resolve_funcs_.erase(resolve_funcs_.begin() + funcs_size, - resolve_funcs_.end()); + TruncateResolveLists(ref_types_size, type_vectors_size, funcs_size); } return result; } +void WastParser::TruncateResolveLists(size_t ref_types_size, + size_t type_vectors_size, + size_t funcs_size) { + resolve_ref_types_.erase(resolve_ref_types_.begin() + ref_types_size, + resolve_ref_types_.end()); + resolve_type_vectors_.erase(resolve_type_vectors_.begin() + type_vectors_size, + resolve_type_vectors_.end()); + resolve_funcs_.erase(resolve_funcs_.begin() + funcs_size, + resolve_funcs_.end()); +} + Result WastParser::ParseModuleFieldImpl(Module* module) { switch (Peek(1)) { case TokenType::Data: return ParseDataModuleField(module); @@ -2286,15 +2291,27 @@ Result WastParser::ParseInstrList(ExprList* exprs) { while (true) { auto pair = PeekPair(); if (IsInstr(pair)) { + // A failed instruction is destroyed along with any block signature it + // parsed, so drop the deferred type resolutions that pointed into it + // before recovering; ParseInstrList swallows the error and keeps going, + // so ParseModuleField never sees the failure and cannot do this itself. + size_t ref_types_size = resolve_ref_types_.size(); + size_t type_vectors_size = resolve_type_vectors_.size(); + size_t funcs_size = resolve_funcs_.size(); if (Succeeded(ParseInstr(&new_exprs))) { exprs->splice(exprs->end(), new_exprs); } else { + TruncateResolveLists(ref_types_size, type_vectors_size, funcs_size); CHECK_RESULT(Synchronize(IsInstr)); } } else if (IsLparAnn(pair)) { + size_t ref_types_size = resolve_ref_types_.size(); + size_t type_vectors_size = resolve_type_vectors_.size(); + size_t funcs_size = resolve_funcs_.size(); if (Succeeded(ParseCodeMetadataAnnotation(&new_exprs))) { exprs->splice(exprs->end(), new_exprs); } else { + TruncateResolveLists(ref_types_size, type_vectors_size, funcs_size); CHECK_RESULT(Synchronize(IsLparAnn)); } } else { @@ -3435,9 +3452,16 @@ Result WastParser::ParseExprList(ExprList* exprs) { WABT_TRACE(ParseExprList); ExprList new_exprs; while (PeekMatchExpr()) { + // See ParseInstrList: a discarded folded expression takes its block + // signature with it, so drop the deferred type resolutions that pointed + // into it before recovering. + size_t ref_types_size = resolve_ref_types_.size(); + size_t type_vectors_size = resolve_type_vectors_.size(); + size_t funcs_size = resolve_funcs_.size(); if (Succeeded(ParseExpr(&new_exprs))) { exprs->splice(exprs->end(), new_exprs); } else { + TruncateResolveLists(ref_types_size, type_vectors_size, funcs_size); CHECK_RESULT(Synchronize(IsExpr)); } } diff --git a/test/parse/bad-ref-type-in-recovered-instr.txt b/test/parse/bad-ref-type-in-recovered-instr.txt new file mode 100644 index 000000000..cc39a82dc --- /dev/null +++ b/test/parse/bad-ref-type-in-recovered-instr.txt @@ -0,0 +1,21 @@ +;;; TOOL: wat2wasm +;;; ARGS: --enable-function-references +;;; ERROR: 1 +(type $t (func)) + +;; A block-style instruction whose signature names a reference type registers +;; a deferred type resolution pointing into the instruction, then fails to +;; parse. ParseInstrList recovers from the failed instruction, so the enclosing +;; func still parses and ParseModuleField never sees a failure; the deferred +;; resolution must not outlive the discarded instruction. +(func block (result (ref $t))) +(func loop (result (ref $t))) +drop ) +(;; STDERR ;;; +out/test/parse/bad-ref-type-in-recovered-instr.txt:11:30: error: unexpected token ), expected end. +(func block (result (ref $t))) + ^ +out/test/parse/bad-ref-type-in-recovered-instr.txt:12:29: error: unexpected token ), expected end. +(func loop (result (ref $t))) + ^ +;;; STDERR ;;) From 65adccf74407bd5ba5dcdfe188c9d5d637ffdc11 Mon Sep 17 00:00:00 2001 From: Aizal Khan Date: Mon, 17 Aug 2026 02:22:15 +0530 Subject: [PATCH 2/2] Use resize to truncate the resolve lists and cover ParseExprList in the test --- include/wabt/wast-parser.h | 9 ++++++--- src/wast-parser.cc | 9 +++------ test/parse/bad-ref-type-in-recovered-instr.txt | 10 ++++++++++ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/include/wabt/wast-parser.h b/include/wabt/wast-parser.h index 8f605568d..dbceb7910 100644 --- a/include/wabt/wast-parser.h +++ b/include/wabt/wast-parser.h @@ -56,10 +56,11 @@ class WastParser { }; struct ResolveRefType { + ResolveRefType() = default; ResolveRefType(Type* target_type, Var var) : target_type(target_type), var(var) {} - Type* target_type; + Type* target_type = nullptr; Var var; }; @@ -74,18 +75,20 @@ class WastParser { typedef std::vector ReferenceVars; struct ResolveTypeVector { + ResolveTypeVector() = default; ResolveTypeVector(TypeVector* target_vector) : target_vector(target_vector) {} - TypeVector* target_vector; + TypeVector* target_vector = nullptr; ReferenceVars vars; }; struct ResolveFunc { + ResolveFunc() = default; ResolveFunc(Func* target_func) : target_func(target_func) {} - Func* target_func; + Func* target_func = nullptr; TypeVector types; ReferenceVars vars; }; diff --git a/src/wast-parser.cc b/src/wast-parser.cc index fd5865f06..f380eb002 100644 --- a/src/wast-parser.cc +++ b/src/wast-parser.cc @@ -1515,12 +1515,9 @@ Result WastParser::ParseModuleField(Module* module) { void WastParser::TruncateResolveLists(size_t ref_types_size, size_t type_vectors_size, size_t funcs_size) { - resolve_ref_types_.erase(resolve_ref_types_.begin() + ref_types_size, - resolve_ref_types_.end()); - resolve_type_vectors_.erase(resolve_type_vectors_.begin() + type_vectors_size, - resolve_type_vectors_.end()); - resolve_funcs_.erase(resolve_funcs_.begin() + funcs_size, - resolve_funcs_.end()); + resolve_ref_types_.resize(ref_types_size); + resolve_type_vectors_.resize(type_vectors_size); + resolve_funcs_.resize(funcs_size); } Result WastParser::ParseModuleFieldImpl(Module* module) { diff --git a/test/parse/bad-ref-type-in-recovered-instr.txt b/test/parse/bad-ref-type-in-recovered-instr.txt index cc39a82dc..6017cbd53 100644 --- a/test/parse/bad-ref-type-in-recovered-instr.txt +++ b/test/parse/bad-ref-type-in-recovered-instr.txt @@ -11,6 +11,13 @@ (func block (result (ref $t))) (func loop (result (ref $t))) drop ) + +;; Folded form of the same bug: the failing expression is an operand of an +;; enclosing folded instruction, so the recovery happens in ParseExprList +;; rather than ParseInstrList. The if registers a deferred resolution for its +;; signature, then fails looking for (then ...) and is discarded, while drop +;; and the enclosing func still parse. +(func (drop (if (result (ref $t)) nop) (nop))) (;; STDERR ;;; out/test/parse/bad-ref-type-in-recovered-instr.txt:11:30: error: unexpected token ), expected end. (func block (result (ref $t))) @@ -18,4 +25,7 @@ out/test/parse/bad-ref-type-in-recovered-instr.txt:11:30: error: unexpected toke out/test/parse/bad-ref-type-in-recovered-instr.txt:12:29: error: unexpected token ), expected end. (func loop (result (ref $t))) ^ +out/test/parse/bad-ref-type-in-recovered-instr.txt:20:35: error: unexpected token nop, expected (. +(func (drop (if (result (ref $t)) nop) (nop))) + ^^^ ;;; STDERR ;;)