CVE-2026-65332
Overview
Background
- WASM structured control result type
- Blocks/if/else/try declare a signature; per spec the block output must have the signature’s result type.
- Subtyping / fallthrough value
- With WASM GC, a fallthrough value may be a subtype of the result type; it must be treated as the declared type, not the subtype.
- externref / non-cell value
- A reference type; a raw non-cell bit pattern accepted where a reference is expected is a type confusion.
Root Cause Analysis
This fixes a WebAssembly type-confusion where a structured control instruction’s result was accepted as a subtype instead of its declared signature type. In WasmFunctionParser::endBlock, when ending an if/else/try(/catch) block the WASM spec requires the block’s output to have the RESULT TYPE from its signature, even when the fallthrough value on the stack is a subtype of that result type.
Pre-patch, the code only forced the signature type for the ’else’ case: const bool shouldForceSignature = ControlType::isElse(data.controlData); and passed that to checkExpressionStack. For other structured control ends (notably try/catch), shouldForceSignature was false, so a fallthrough value of a NARROWER subtype was accepted and left on the stack as-is rather than being widened to the declared result type. Downstream code then treats that value as the declared (wider) type — a type confusion; the reproducer builds a try/catch that yields a non-cell externref (a raw float64 bit pattern) where a reference type is expected, so a non-object bit pattern is used as a reference.
The fix always forces the signature result type: WASM_FAIL_IF_HELPER_FAILS(checkExpressionStack(data.controlData, true)) for all structured control ends.
The restored invariant is that a structured control instruction’s output type is its signature result type, so a subtype fallthrough cannot smuggle a narrower/mistyped value past validation.
Attack Path
- Craft a try/catch block Build a WASM module whose try/catch (structured control) block declares a result type but yields a subtype fallthrough value.
- Bypass the type check Because endBlock did not force the signature type for try/catch, the narrower value is accepted on the stack.
- Produce a mistyped reference Yield a non-cell externref (a crafted float64 bit pattern) where a reference type is expected.
- Type confusion Downstream use treats the raw bit pattern as a valid reference, a memory-safety violation crashing the WebContent process.
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
IPIntGenerator/FunctionParser endBlock (checkExpressionStack)Source/JavaScriptCore/wasm/WasmFunctionParser.h |
modified | Always forces the signature result type when ending a structured control instruction (checkExpressionStack(..., true)) instead of only for 'else', so a subtype fallthrough cannot be accepted as the declared type. |
Files Changed
JSTests/wasm/regress/try-catch-result-type.jsSource/JavaScriptCore/wasm/WasmFunctionParser.h
Audit Directions
- Structured-control result typingAudit WasmFunctionParser end-of-block handling for other cases that skip forcing the signature result type or that special-case only ’else'.
- Subtype-to-supertype coercionsGrep WASM validation for places a subtype value is left on the stack without being widened to the declared block/return type.