-
Notifications
You must be signed in to change notification settings - Fork 996
Description
RangeError: Start offset is negative when processing large data in WebAssembly
Description
When working with large data in the browser, the following errors occurs:
RangeError: Start offset -2147452000 is outside the bounds of the buffer
at new DataView
at loadString
at syscall/js.stringVal
RangeError: Start offset -2018731584 is outside the bounds of the buffer
at new Uint8Array
at syscall/js.copyBytesToJS
Root Cause
The issue occurs because WebAssembly memory pointers are unsigned 32-bit integers, but JavaScript interprets them as signed integers in certain operations. When pointers fall into the upper half of the 32-bit address space (starting from bit 2³¹), JavaScript treats them as negative numbers.
For example:
- Unsigned representation:
2147515296(valid address) - Signed representation:
-2147452000(after overflow)
This causes DataView and typed array constructors to throw a RangeError since they don't accept negative offsets.
Affected Code
The issue affects the loadString, loadValue, storeValue, loadSlice function and other places where trying to set or get uint value:
const loadString = (ptr, len) => {
return decoder.decode(new DataView(this._inst.exports.memory.buffer, ptr, len)); // <- ptr could be negative
};
const storeValue = (addr, v) => {
let v_ref = boxValue(v);
mem().setBigUint64(addr, v_ref, true); // <- this addr could be negavtive
}
Proposed Solution
Use the unsigned right shift operator >>> 0 to convert values to unsigned 32-bit integers:
const loadString = (ptr, len) => {
// Convert to unsigned 32-bit integers to handle potential negative values from WASM
return decoder.decode(new DataView(this._inst.exports.memory.buffer, ptr >>> 0, len >>> 0));
};
The same fix should be applied to similar functions that create typed arrays or DataViews from WASM memory:
const loadSlice = (array, len, cap) => {
// Convert to unsigned 32-bit integers to handle potential negative values from WASM
return new Uint8Array(this._inst.exports.memory.buffer, array >>> 0, len >>> 0);
};
Reproduction
- Load a TinyGo-compiled WebAssembly module
- Process data that causes memory allocation in the upper half of the 32-bit address space
- Call any syscall/js function that uses
loadStringor similar memory access functions - Observe the RangeError
Environment
- Browser: Any (Chrome, Firefox, Safari)
- WebAssembly: TinyGo-compiled modules
- File:
wasm_exec.js(TinyGo runtime)
Related Issues
Similar issue but fix was done only for one place
#4763