← All issues

[JSC] Due to IPInt, we need to ensure that wasm multi-memory does not support memory64

The memory64 check that only looked at the memory it was holding.

Component: JSC WebAssembly | d319ee7

Wasm's multi-memory proposal lets a module declare several linear memories, while memory64 lets an individual memory use 64-bit addressing. WebKit currently forbids mixing the two — a memory64 module must have exactly one memory — because IPInt, the in-place wasm interpreter, hardcodes the address width from memory 0 in its bounds-checking fast path and applies it uniformly to every memory access regardless of which memory is being touched.

Source/JavaScriptCore/wasm/WasmSectionParser.cpp

- if (isMemory64)
- WASM_PARSER_FAIL_IF(m_info->memoryCount(), "if using memory64 then multiple memories are illegal for now");
+ if (m_info->memoryCount())
+ WASM_PARSER_FAIL_IF(isMemory64 || m_info->memory(0).isMemory64(), "if using memory64 then multiple memories are illegal for now");

JSTests/wasm/stress/memory64-multi-memory-rejected.js

+await assertRejected(`(module (memory i64 1) (memory 1))`);
+await assertRejected(`(module (memory 1) (memory i64 1))`);

The original guard only fired when the memory currently being parsed was 64-bit. A leading memory i64 saw memoryCount() == 0 and passed; a trailing memory32 never set isMemory64, so it passed too. The new form inverts the condition — it keys on "there is already a memory" and then rejects if either the current memory or memory 0 is 64-bit — closing the declaration-order dependence. The two-line test pins both orderings.

Before the fix, (module (memory i64 1) (memory 1)) validated cleanly, and IPInt would then bounds-check the second 32-bit memory using 64-bit-derived address-width logic pulled from memory 0. That mismatch is a plausible route to an OOB read/write primitive in the interpreter tier, reachable from a crafted module with no exotic setup.

The pattern is a validator invariant that assumes a property of memory 0 generalizes to every declared memory, in a tier that reads only memory 0 at runtime. Narrow: enumerate the other IPInt and wasm-validator invariants keyed on memory 0 — shared vs. non-shared, page limits, growability — and check each one validates its assumption across all declared memories rather than sampling the first. Wider: ask the same question of the JIT tiers: does BBQ/OMG have an equivalent single-source-of-truth issue for other per-memory attributes, where a compile-time constant derived from memory 0 is applied to accesses against memory n? Widest: any validator that enforces a whole-module constraint through a per-item loop is order-sensitive unless the check is expressed over the accumulated set rather than the item in hand; the review tell is a validation predicate inside a parse loop that reads only the loop variable when the constraint it enforces spans siblings.