Source code

Revision control

Copy as Markdown

Other Tools

// |jit-test| --setpref=wasm_memory_debugging;
// Tests for out-of-bounds error messages, including the detailed form that
// includes memory index and byte offset on 64-bit platforms.
// We only support printing a useful offset message with signal handling.
const hasOffsetMessage = wasmHugeMemoryEnabled();
function oobPattern(memIdx, byteOffset) {
if (hasOffsetMessage) {
return new RegExp(
`out of bounds: memory ${memIdx} access at memory address ${byteOffset}`
);
}
return /index out of bounds/;
}
// Cover all combinations of:
// load/store * memory 0/1 * no-offset/offset=8
// Memory 0 has 1 page, memory 1 has 2 pages.
let {
load0, load0_off, store0, store0_off,
load1, load1_off, store1, store1_off,
grow0, grow1,
} = wasmEvalText(`
(module
(memory 1)
(memory 2)
(func (export "load0") (param i32) (result i32)
(i32.load 0 (local.get 0)))
(func (export "load0_off") (param i32) (result i32)
(i32.load 0 offset=8 (local.get 0)))
(func (export "store0") (param i32) (param i32)
(i32.store 0 (local.get 0) (local.get 1)))
(func (export "store0_off") (param i32) (param i32)
(i32.store 0 offset=8 (local.get 0) (local.get 1)))
(func (export "load1") (param i32) (result i32)
(i32.load 1 (local.get 0)))
(func (export "load1_off") (param i32) (result i32)
(i32.load 1 offset=8 (local.get 0)))
(func (export "store1") (param i32) (param i32)
(i32.store 1 (local.get 0) (local.get 1)))
(func (export "store1_off") (param i32) (param i32)
(i32.store 1 offset=8 (local.get 0) (local.get 1)))
(func (export "grow0") (param i32) (result i32)
(memory.grow 0 (local.get 0)))
(func (export "grow1") (param i32) (result i32)
(memory.grow 1 (local.get 0)))
)
`).exports;
const p = PageSizeInBytes;
const RuntimeError = WebAssembly.RuntimeError;
// Memory 0 has 1 page, memory 1 has 2 pages.
assertErrorMessage(() => load0(p), RuntimeError, oobPattern(0, p));
assertErrorMessage(() => load0_off(p), RuntimeError, oobPattern(0, p + 8));
assertErrorMessage(() => store0(p, 0), RuntimeError, oobPattern(0, p));
assertErrorMessage(() => store0_off(p, 0), RuntimeError, oobPattern(0, p + 8));
assertErrorMessage(() => load1(2 * p), RuntimeError, oobPattern(1, 2 * p));
assertErrorMessage(() => load1_off(2 * p), RuntimeError, oobPattern(1, 2 * p + 8));
assertErrorMessage(() => store1(2 * p, 0), RuntimeError, oobPattern(1, 2 * p));
assertErrorMessage(() => store1_off(2 * p, 0), RuntimeError, oobPattern(1, 2 * p + 8));
// Grow each memory by one page, then re-run the OOB checks at the new boundaries.
assertEq(grow0(1), 1);
assertEq(grow1(1), 2);
// Memory 0 now has 2 pages, memory 1 now has 3 pages.
assertErrorMessage(() => load0(2 * p), RuntimeError, oobPattern(0, 2 * p));
assertErrorMessage(() => load0_off(2 * p), RuntimeError, oobPattern(0, 2 * p + 8));
assertErrorMessage(() => store0(2 * p, 0), RuntimeError, oobPattern(0, 2 * p));
assertErrorMessage(() => store0_off(2 * p, 0), RuntimeError, oobPattern(0, 2 * p + 8));
assertErrorMessage(() => load1(3 * p), RuntimeError, oobPattern(1, 3 * p));
assertErrorMessage(() => load1_off(3 * p), RuntimeError, oobPattern(1, 3 * p + 8));
assertErrorMessage(() => store1(3 * p, 0), RuntimeError, oobPattern(1, 3 * p));
assertErrorMessage(() => store1_off(3 * p, 0), RuntimeError, oobPattern(1, 3 * p + 8));