WebAssembly / WebAssembly/wabt

wat2wasm/wast2json: assertion failure (!IsReferenceWithIndex()) parsing a typed reference in a GC struct/array field

Open
#2,750 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C++
Stars
8.1k
Forks
827
Avg merge
4d 6h
Merged PRs (30d)
18

Description

Summary

The WAT text parser aborts with an assertion failure when a GC struct or array field has a typed reference element type ((ref N) / (ref null N)). WastParser::ParseField builds the field's Type with the single-argument Type(Type::Enum) constructor, which discards the type index and asserts !IsReferenceWithIndex().

This is reachable from any tool/library entry point that parses WebAssembly text via ParseWatModule / ParseWastScript: wat2wasm, wast2json, spectest-interp, and any application linking libwabt.

  • Debug/assert builds: abort() → denial of service (CWE-617, reachable assertion).
  • Release (NDEBUG) builds: the assertion is compiled out and the field silently gets a Type with type_index_ = 0 — the referenced type index is lost (incorrect parse result / type confusion).

Tested on main @ 03a00a1, Ubuntu 24.04, clang 18.1.3.

Steps to reproduce

$ echo '(module (type (struct (field (ref 0)))))' | wat2wasm --enable-gc --enable-function-references -
wat2wasm: .../include/wabt/type.h:75: wabt::Type::Type(wabt::Type::Enum): Assertion `!IsReferenceWithIndex()' failed.
Aborted (core dumped)

$ echo '(module (type (array (ref 0))))' | wat2wasm --enable-gc --enable-function-references -
Aborted (core dumped)

The same inputs also crash wast2json --enable-all.

Required feature flags

Both --enable-gc (for struct/array) and --enable-function-references (for the (ref N) syntax) are required.

What does / does not trigger it
Input inside (module (type ...)) flags result
(struct (field (ref 0))) gc + function-references abort
(array (ref 0)) gc + function-references abort
(struct (field (mut (ref 0)))) gc + function-references abort
(struct (field (ref 0))) gc only clean parse error (ok)
(struct (field i32)) gc + function-references ok
(struct (field funcref)) (non-indexed ref) gc + function-references ok

Only indexed typed references crash; non-reference and non-indexed reference types are fine.

Affected tools / functions

libwabtWastParser::ParseField (reached by ParseWatModule and ParseWastScript). Tools: wat2wasm, wast2json, spectest-interp.

Root cause

src/wast-parser.cc, WastParser::ParseField() → lambda parse_mut_valuetype (lines ~1785–1792):

Var type;
CHECK_RESULT(ParseValueType(&type));
field->type = Type(type.opt_type());   // single-arg ctor drops the type index

ParseValueType may yield a typed reference (Type::Ref / Type::RefNull) whose opt_type() IsReferenceWithIndex(). Passing it to the single-arg constructor triggers the assert in include/wabt/type.h:74:

Type(Enum e) : enum_(e), type_index_(0) { assert(!IsReferenceWithIndex()); }

The sibling code in the same file already handles this correctly with the two-argument constructor:

// src/wast-parser.cc:1031  (ParseValueTypeList)
out_type_list->push_back(Type(type.opt_type(), kInvalidIndex));

So ParseField is simply missing the index argument that the rest of the parser uses.

Suggested fix

Preserve the reference's type index instead of dropping it — either mirror line 1031:

-   field->type = Type(type.opt_type());
+   field->type = Type(type.opt_type(), kInvalidIndex);

(applied to both occurrences), or, better, resolve the parsed Var to a fully-typed reference the way other call sites do (VarToType(...), used for table element types), so that (ref N) keeps its actual target index N. The minimal change stops the crash; the VarToType-based change additionally fixes the silent index loss in release builds.

Discovery

Found via libFuzzer + ASAN/UBSAN with a custom wast2json harness mirroring src/tools/wast2json.cc.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/wast-parser.cc at WastParser::ParseField and compare parse_mut_valuetype with ParseValueTypeList around line 1031; inspect include/wabt/type.h to understand the assertion. Reproduce with the listed wat2wasm and wast2json inputs, then verify typed reference indices are preserved and both tools parse struct and array fields without aborting.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, wasm
Domain
compilers, devtools
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.