← All issues

[19] [JSC Wasm IPInt] Widen frame-size accumulator in IPInt::finalize

Severity: High | Component: JSC Wasm IPInt | 9161e71

Rated High because the diff corrects a unit/scale mismatch in IPIntGenerator::finalize()'s computation of m_maxFrameSizeInV128; pre-fix, the stack-overflow probe used a value that diverged from actual frame usage, so a Wasm module with many locals could pass the probe while exceeding remaining native stack — an unchecked stack overflow in the worker thread.

IPIntGenerator::finalize() recomputes m_maxFrameSizeInV128 consistently with the runtime layout (LOCAL_SIZE = 16), ensuring the value matches the v128-slot interpretation used by the prologue.

Source/JavaScriptCore/wasm/wasmllint/IPIntGenerator.cpp

- m_maxFrameSizeInV128 = computeFrameSize(); // unit mismatch
+ m_maxFrameSizeInV128 = computeFrameSizeInV128(); // matches runtime LOCAL_SIZE

Stack-overflow check bypass via unit/scale miscomputation: the prologue stack probe and the actual frame allocation diverged under many-locals modules.

The frame-size accumulator is computed in v128 units consistently with IPIntLocal's 16-byte slot. The runtime prologue stack-check now sees a value that matches actual usage.

The IPInt header declares IPIntLocal as 16 bytes (LOCAL_SIZE = 16); each local occupies one v128 in the IPInt frame. IPIntGenerator::finalize() is the final compilation step for the IPInt tier and computes the per-function frame size used by the prologue stack-overflow check.

Pre-fix, the finalize step computed m_maxFrameSizeInV128 in a unit that did not match the runtime layout. If the value was computed in bytes (or in 8-byte units) but interpreted as v128 slots — or vice versa — the prologue probe diverged from actual frame usage.

The test sweeps 8000–35000 i64 locals × 16 B = 128 KB–560 KB of frame. The divergence is large enough that the probe passes while the actual usage exceeds the remaining native stack — an unchecked stack overflow in the worker thread. The downstream primitive is stack-blown memory access in a Wasm-compiled function, including overwrite of saved registers and return addresses below the stack guard.

This weakens the IPInt stack-overflow invariant for many-locals Wasm modules.