[JSC] Fix wasm type parsing regression by making RTT formal canonicalized types
94e35cf parent — see above; this is 103a40a
// JSTests/wasm/stress/cross-module-rtt-identity.js
// Two separately compiled modules declare the same recursive struct type.
// Isorecursive canonicalization must map both to the same RTT pointer
// so that a ref.cast in module B on a value produced by module A succeeds.
const { makeLeaf, leafVal } = new WebAssembly.Instance(new WebAssembly.Module(M_A)).exports;
const { castAndRead } = new WebAssembly.Instance(new WebAssembly.Module(M_B)).exports;
const b = castAndRead(leaf);
if (b !== v)
throw new Error(`B castAndRead(${v}): expected ${v}, got ${b}`);
WebAssembly GC adds heap-allocated structs, arrays, and recursive types to wasm. Runtime Types (RTTs) are the objects that carry type identity; they are compared by pointer equality when executing ref.cast and subtype checks. Correct operation requires isorecursive canonicalization — two independently compiled modules declaring structurally identical recursive types must receive the same canonical RTT pointer, so cross-module ref.cast succeeds as the spec requires.
This commit removes TypeDefinition entirely from JSC's WebAssembly GC type system and unifies all runtime type representation around RTT. It introduces TypeSectionState to hold temporary parsing structures (Subtype, RecursionGroup, Projection) that live only during type-section parsing, after which everything is converted to canonical RTTs registered with the process-wide TypeInformation singleton. RTTs now hold RefPtr references to sibling RTTs, intentionally forming refcount cycles for mutually-recursive types that are permanently leaked (matching V8's current approach), pending a future cycle collector. The commit also fixes cross-module isorecursive RTT canonicalization and a performance regression from repeated traversal during GC structure construction (introduced in 312331@main).
Significance
This is a ground-up rewrite of the Wasm GC type infrastructure, and a single missed pointer escape from the temporary TypeSectionState becomes a use-after-free reachable by every cross-module ref.cast.
Audit directions
- Canonicalization hash/equality over recursive types.
canonicalizeRecursionGroupImplandcanonicalizeSingletonImpl(inWasmTypeDefinition.cpp) implement the deduplication that underpins every cross-moduleref.cast. The hash and equality functions (hashRTTForRecGroup,equalRTTsForRecGroup) operate over recursive structures using placeholder references during construction — subtle mistakes can make structurally distinct types compare equal (type confusion) or equivalent types compare unequal (breaking valid casts via fallback paths). TypeSectionStatepointer escape.Subtype,Projection, andRecursionGroupobjects are allocated in aSegmentedVectorinsideTypeSectionStateand swept when type-section parsing ends. The ~100-file refactor must ensure no pointer to these objects survives into an RTT,ModuleInformation, or any other longer-lived structure. A single missed raw pointer becomes a UAF after parsing.- RTT cycle anchoring completeness. The commit transitions from raw
TypeIndexreferences toRefPtr<const RTT>anchor slots (TypeSlot::rttAnchor,StructFieldEntry::rttAnchor,RTTArrayPayload::m_elementTypeAnchor) to keep sibling RTTs alive through cycles. Any field in a payload that holds a reference to another RTT without a corresponding anchorRefPtrreintroduces the dangling pointer the commit is designed to fix. ref.cast/ subtype check correctness.isSubRTTandisStrictSubRTTrely on canonical RTT pointer equality. The replacement oftypeIndexFromFunctionIndexSpacewithrttFromFunctionIndexSpaceacross BBQ, OMG, IPInt, B3, and DFG/FTL is mechanical but vast. An off-by-one or incorrect RTT selection at any callsite in the cast or call-ref path is a type-confusion primitive.