[JSC] Active element segment offsets are truncated for a table64
An out-of-bounds table offset didn't trap — it wrote to slot zero.
Component: JSC WebAssembly | e942b93
WebAssembly tables hold typed references such as funcref, used for indirect calls, and active element segments populate table slots at instantiation time using an offset expression that can be a constant, an imported global, or a constant expression. The table64 proposal widens table indices from i32 to i64, so offsets must be carried and compared as 64-bit values end to end — any place that narrows the offset before the bounds check reintroduces the exact wraparound class the wider type was meant to prevent.
Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp
forEachActiveElement truncated the 64-bit segment offset to uint32_t before validating it against the table size. The fix widens elementIndex to uint64_t and branches all three offset sources — imported global, constant, and constant expression — on the table's declared address type, loading an i64 global and evaluating against Wasm::Types::I64 when the table is 64-bit while preserving the explicit 32-bit narrowing for i32 tables.
Significance
An offset of 4294967296 truncated to 0, so a segment that should have trapped as out-of-bounds instead silently wrote function references into slot 0 of the table. The source of that offset can be attacker-controlled module data or an imported global, making this a table-content corruption reachable at instantiation time. The commit message attributes the same class to an earlier fix in 317633@main on a different code path, which is what marks this as a recurrence rather than a one-off.
Audit directions
The pattern is 32-bit truncation under 64-bit addressing, narrowing a value before the bounds check that value was meant to survive — and it has now been found twice in sibling code paths, which is the strongest available signal that more instances exist. Narrow: audit every place constValue(), loadI64Global, or constant-expression evaluation results feed into table or memory indexing in WebAssemblyModuleRecord, and confirm the i64 path is preserved end to end rather than silently cast down. Wider: extend to the neighbouring operations that share the offset-expression machinery — table.init, table.copy, and active data segments for memory64 — since they consume the same constant-expression evaluator and the same global-load helpers. Widest: whenever a type is widened across a proposal boundary, the risk concentrates not at the new call sites but at the old ones that still compile fine with the narrower local; the review tell is a static_cast<uint32_t> (or a uint32_t local) on any value that a validation comparison downstream treats as authoritative.