summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/wasm/passive-segs-boundary.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/wasm/passive-segs-boundary.js')
-rw-r--r--js/src/jit-test/tests/wasm/passive-segs-boundary.js442
1 files changed, 442 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/wasm/passive-segs-boundary.js b/js/src/jit-test/tests/wasm/passive-segs-boundary.js
new file mode 100644
index 0000000000..18dee4a303
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/passive-segs-boundary.js
@@ -0,0 +1,442 @@
+// Perform a test which,
+//
+// * if errKind is defined, is expected to fail with an exception
+// characterised by errKind and errText.
+//
+// * if errKind is undefined, is expected to succeed, in which case errKind
+// and errText are ignored.
+//
+// The function body will be [insn1, insn2] and is constructed according to
+// four booleans:
+//
+// * isMem controls whether the module is constructed with memory or
+// table initializers.
+//
+// * haveStorage determines whether there is actually a memory or table to
+// work with.
+//
+// * haveInitA controls whether active initializers are added.
+//
+// * haveInitP controls whether passive initializers are added.
+
+function do_test(insn1, insn2, errKind, errText,
+ isMem, haveStorage, haveInitA, haveInitP)
+{
+ let preamble;
+ if (isMem) {
+ let mem_def = haveStorage ? "(memory 1 1)" : "";
+ let mem_ia1 = `(data (i32.const 2) "\\03\\01\\04\\01")`;
+ let mem_ia2 = `(data (i32.const 12) "\\07\\05\\02\\03\\06")`;
+ let mem_ip1 = `(data "\\02\\07\\01\\08")`;
+ let mem_ip2 = `(data "\\05\\09\\02\\07\\06")`;
+ let mem_init = ``;
+ if (haveInitA && haveInitP)
+ mem_init = `${mem_ia1} ${mem_ip1} ${mem_ia2} ${mem_ip2}`;
+ else if (haveInitA && !haveInitP) mem_init = `${mem_ia1} ${mem_ia2}`;
+ else if (!haveInitA && haveInitP) mem_init = `${mem_ip1} ${mem_ip2}`;
+ preamble
+ = `;; -------- Memories --------
+ ${mem_def}
+ ;; -------- Memory initialisers --------
+ ${mem_init}
+ `;
+ } else {
+ let tab_def = haveStorage ? "(table 30 30 funcref)" : "";
+ let tab_ia1 = `(elem (i32.const 2) 3 1 4 1)`;
+ let tab_ia2 = `(elem (i32.const 12) 7 5 2 3 6)`;
+ let tab_ip1 = `(elem func 2 7 1 8)`;
+ let tab_ip2 = `(elem func 5 9 2 7 6)`;
+ let tab_init = ``;
+ if (haveInitA && haveInitP)
+ tab_init = `${tab_ia1} ${tab_ip1} ${tab_ia2} ${tab_ip2}`;
+ else if (haveInitA && !haveInitP) tab_init = `${tab_ia1} ${tab_ia2}`;
+ else if (!haveInitA && haveInitP) tab_init = `${tab_ip1} ${tab_ip2}`;
+ preamble
+ = `;; -------- Tables --------
+ ${tab_def}
+ ;; -------- Table initialisers --------
+ ${tab_init}
+ ;; ------ Functions (0..9) referred by the table/esegs ------
+ (func (result i32) (i32.const 0))
+ (func (result i32) (i32.const 1))
+ (func (result i32) (i32.const 2))
+ (func (result i32) (i32.const 3))
+ (func (result i32) (i32.const 4))
+ (func (result i32) (i32.const 5))
+ (func (result i32) (i32.const 6))
+ (func (result i32) (i32.const 7))
+ (func (result i32) (i32.const 8))
+ (func (result i32) (i32.const 9))
+ `;
+ }
+
+ let txt = "(module\n" + preamble +
+ `;; -------- testfn --------
+ (func (export "testfn")
+ ${insn1}
+ ${insn2}
+ )
+ )`;
+
+ if (!!errKind) {
+ assertErrorMessage(
+ () => {
+ let inst = wasmEvalText(txt);
+ inst.exports.testfn();
+ },
+ errKind,
+ errText
+ );
+ } else {
+ let inst = wasmEvalText(txt);
+ assertEq(undefined, inst.exports.testfn());
+ }
+}
+
+
+// Some handy specialisations of do_test().
+
+function mem_test(insn1, insn2, errKind, errText,
+ haveStorage=true, haveInitA=true, haveInitP=true) {
+ do_test(insn1, insn2, errKind, errText,
+ /*isMem=*/true, haveStorage, haveInitA, haveInitP);
+}
+
+function mem_test_nofail(insn1, insn2,
+ haveStorage=true, haveInitA=true, haveInitP=true) {
+ do_test(insn1, insn2, undefined, undefined,
+ /*isMem=*/true, haveStorage, haveInitA, haveInitP);
+}
+
+function tab_test(insn1, insn2, errKind, errText,
+ haveStorage=true, haveInitA=true, haveInitP=true) {
+ do_test(insn1, insn2, errKind, errText,
+ /*isMem=*/false, haveStorage, haveInitA, haveInitP);
+}
+
+function tab_test_nofail(insn1, insn2,
+ haveStorage=true, haveInitA=true, haveInitP=true) {
+ do_test(insn1, insn2, undefined, undefined,
+ /*isMem=*/false, haveStorage, haveInitA, haveInitP);
+}
+
+
+//---- memory.{drop,init,copy} -------------------------------------------------
+
+// The tested semantics for memory.drop, in the case where there's no memory,
+// are as follows. table.drop is analogous.
+//
+// no memory, no data segments:
+// drop with any args -> fail OOB
+// [because there's nothing to drop]
+//
+// no memory, data segments, at least one of which is active:
+// -> always fails, regardless of insns
+// [because active segments implicitly reference memory 0]
+//
+// no memory, data segments, all of which are passive:
+// drop, segment index is OOB -> fail OOB
+// [because it refers to non existent segment]
+//
+// drop, segment index is IB -> OK
+
+// drop with no memory and no data segments
+mem_test("data.drop 0", "",
+ WebAssembly.CompileError, /(data.drop segment index out of range)|(unknown data segment 0)/,
+ /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/false);
+
+// drop with no memory but with both passive and active segments, ix in range
+// and refers to a passive segment
+mem_test("data.drop 3", "",
+ WebAssembly.CompileError,
+ /active data segment requires a memory section/,
+ /*haveStorage=*/false, /*haveInitA=*/true, /*haveInitP=*/true);
+
+// drop with no memory but with passive segments only, ix out of range
+mem_test("data.drop 2", "",
+ WebAssembly.CompileError, /(data.drop segment index out of range)|(unknown data segment 2)/,
+ /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/true);
+
+// drop with no memory but with passive segments only, ix in range
+mem_test_nofail("data.drop 1", "",
+ /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/true);
+
+
+// init with no memory and no data segs
+mem_test("(memory.init 1 (i32.const 1234) (i32.const 1) (i32.const 1))", "",
+ WebAssembly.CompileError, /(can't touch memory without memory)|(unknown memory 0)/,
+ /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/false);
+
+// drop with data seg ix out of range
+mem_test("data.drop 4", "",
+ WebAssembly.CompileError, /(data.drop segment index out of range)|(unknown data segment 4)/);
+
+// init with data seg ix out of range
+mem_test("(memory.init 4 (i32.const 1234) (i32.const 1) (i32.const 1))", "",
+ WebAssembly.CompileError, /(memory.init segment index out of range)|(unknown data segment 4)/);
+
+// drop with data seg ix indicating an active segment
+mem_test("data.drop 2", "");
+
+// init with data seg ix indicating an active segment
+mem_test("(memory.init 2 (i32.const 1234) (i32.const 1) (i32.const 1))", "",
+ WebAssembly.RuntimeError, /index out of bounds/);
+
+// init, using a data seg ix more than once is OK
+mem_test_nofail(
+ "(memory.init 1 (i32.const 1234) (i32.const 1) (i32.const 1))",
+ "(memory.init 1 (i32.const 4321) (i32.const 1) (i32.const 1))");
+
+// drop, then drop
+mem_test("data.drop 1",
+ "data.drop 1");
+
+// drop, then init
+mem_test("data.drop 1",
+ "(memory.init 1 (i32.const 1234) (i32.const 1) (i32.const 1))",
+ WebAssembly.RuntimeError, /index out of bounds/);
+
+// init: seg ix is valid passive, but length to copy > len of seg
+mem_test("",
+ "(memory.init 1 (i32.const 1234) (i32.const 0) (i32.const 5))",
+ WebAssembly.RuntimeError, /index out of bounds/);
+
+// init: seg ix is valid passive, but implies copying beyond end of seg
+mem_test("",
+ "(memory.init 1 (i32.const 1234) (i32.const 2) (i32.const 3))",
+ WebAssembly.RuntimeError, /index out of bounds/);
+
+// init: seg ix is valid passive, but implies copying beyond end of dst
+mem_test("",
+ "(memory.init 1 (i32.const 0xFFFE) (i32.const 1) (i32.const 3))",
+ WebAssembly.RuntimeError, /index out of bounds/);
+
+// init: seg ix is valid passive, zero len, but src offset out of bounds at the
+// edge
+mem_test("",
+ "(memory.init 1 (i32.const 1234) (i32.const 4) (i32.const 0))");
+
+// init: seg ix is valid passive, zero len, but src offset out of bounds one
+// past the edge
+mem_test("",
+ "(memory.init 1 (i32.const 1234) (i32.const 5) (i32.const 0))",
+ WebAssembly.RuntimeError, /index out of bounds/);
+
+// init: seg ix is valid passive, zero len, but dst offset out of bounds at the
+// edge
+mem_test("",
+ "(memory.init 1 (i32.const 0x10000) (i32.const 2) (i32.const 0))");
+
+// init: seg ix is valid passive, zero len, but dst offset out of bounds one
+// past the edge
+mem_test("",
+ "(memory.init 1 (i32.const 0x10001) (i32.const 2) (i32.const 0))",
+ WebAssembly.RuntimeError, /index out of bounds/);
+
+// drop: too many args
+mem_test("data.drop 1 (i32.const 42)", "",
+ WebAssembly.CompileError,
+ /(unused values not explicitly dropped by end of block)|(values remaining on stack at end of block)/);
+
+// init: too many args
+mem_test("(memory.init 1 (i32.const 1) (i32.const 1) (i32.const 1) (i32.const 1))",
+ "",
+ WebAssembly.CompileError, /(unused values)|(values remaining on stack at end of block)/);
+
+// init: too few args
+mem_test("(memory.init 1 (i32.const 1) (i32.const 1))", "",
+ WebAssembly.CompileError,
+ /(popping value from empty stack)|(expected i32 but nothing on stack)/);
+
+// invalid argument types
+{
+ const tys = ['i32', 'f32', 'i64', 'f64'];
+
+ for (let ty1 of tys) {
+ for (let ty2 of tys) {
+ for (let ty3 of tys) {
+ if (ty1 == 'i32' && ty2 == 'i32' && ty3 == 'i32')
+ continue; // this is the only valid case
+ let i1 = `(memory.init 1 (${ty1}.const 1) (${ty2}.const 1) (${ty3}.const 1))`;
+ mem_test(i1, "", WebAssembly.CompileError, /type mismatch/);
+ }}}
+}
+
+//
+//---- table.{drop,init} --------------------------------------------------
+
+// drop with no tables and no elem segments
+tab_test("elem.drop 0", "",
+ WebAssembly.CompileError,
+ /(element segment index out of range for elem.drop)|(segment index out of bounds)/,
+ /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/false);
+
+// drop with no tables but with both passive and active segments, ix in range
+// and refers to a passive segment
+tab_test("elem.drop 3", "",
+ WebAssembly.CompileError,
+ /active elem segment requires a table/,
+ /*haveStorage=*/false, /*haveInitA=*/true, /*haveInitP=*/true);
+
+// drop with no tables but with passive segments only, ix out of range
+tab_test("elem.drop 2", "",
+ WebAssembly.CompileError,
+ /(element segment index out of range for elem.drop)|(segment index out of bounds)/,
+ /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/true);
+
+// drop with no tables but with passive segments only, ix in range
+tab_test_nofail("elem.drop 1", "",
+ /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/true);
+
+
+// init with no table
+tab_test("(table.init 1 (i32.const 12) (i32.const 1) (i32.const 1))", "",
+ WebAssembly.CompileError, /(table index out of range)|(table index out of bounds)/,
+ /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/false);
+
+// drop with elem seg ix out of range
+tab_test("elem.drop 4", "",
+ WebAssembly.CompileError, /(element segment index out of range for elem.drop)|(segment index out of bounds)/);
+
+// init with elem seg ix out of range
+tab_test("(table.init 4 (i32.const 12) (i32.const 1) (i32.const 1))", "",
+ WebAssembly.CompileError, /(table.init segment index out of range)|(segment index out of bounds)/);
+
+// drop with elem seg ix indicating an active segment
+tab_test("elem.drop 2", "");
+
+// init with elem seg ix indicating an active segment
+tab_test("(table.init 2 (i32.const 12) (i32.const 1) (i32.const 1))", "",
+ WebAssembly.RuntimeError, /index out of bounds/);
+
+// init, using an elem seg ix more than once is OK
+tab_test_nofail(
+ "(table.init 1 (i32.const 12) (i32.const 1) (i32.const 1))",
+ "(table.init 1 (i32.const 21) (i32.const 1) (i32.const 1))");
+
+// drop, then drop
+tab_test("elem.drop 1",
+ "elem.drop 1");
+
+// drop, then init
+tab_test("elem.drop 1",
+ "(table.init 1 (i32.const 12) (i32.const 1) (i32.const 1))",
+ WebAssembly.RuntimeError, /index out of bounds/);
+
+// init: seg ix is valid passive, but length to copy > len of seg
+tab_test("",
+ "(table.init 1 (i32.const 12) (i32.const 0) (i32.const 5))",
+ WebAssembly.RuntimeError, /index out of bounds/);
+
+// init: seg ix is valid passive, but implies copying beyond end of seg
+tab_test("",
+ "(table.init 1 (i32.const 12) (i32.const 2) (i32.const 3))",
+ WebAssembly.RuntimeError, /index out of bounds/);
+
+// init: seg ix is valid passive, but implies copying beyond end of dst
+tab_test("",
+ "(table.init 1 (i32.const 28) (i32.const 1) (i32.const 3))",
+ WebAssembly.RuntimeError, /index out of bounds/);
+
+// init: seg ix is valid passive, zero len, but src offset out of bounds at the
+// edge
+tab_test("",
+ "(table.init 1 (i32.const 12) (i32.const 4) (i32.const 0))");
+
+// init: seg ix is valid passive, zero len, but src offset out of bounds one
+// past the edge
+tab_test("",
+ "(table.init 1 (i32.const 12) (i32.const 5) (i32.const 0))",
+ WebAssembly.RuntimeError, /index out of bounds/);
+
+// init: seg ix is valid passive, zero len, but dst offset out of bounds
+tab_test("",
+ "(table.init 1 (i32.const 30) (i32.const 2) (i32.const 0))");
+
+// init: seg ix is valid passive, zero len, but dst offset out of bounds one
+// past the edge
+tab_test("",
+ "(table.init 1 (i32.const 31) (i32.const 2) (i32.const 0))",
+ WebAssembly.RuntimeError, /index out of bounds/);
+
+// drop: too many args
+tab_test("elem.drop 1 (i32.const 42)", "",
+ WebAssembly.CompileError,
+ /(unused values not explicitly dropped by end of block)|(values remaining on stack at end of block)/);
+
+// init: too many args
+tab_test("(table.init 1 (i32.const 1) (i32.const 1) (i32.const 1) (i32.const 1))",
+ "",
+ WebAssembly.CompileError, /(unused values)|(values remaining on stack at end of block)/);
+
+// init: too few args
+tab_test("(table.init 1 (i32.const 1) (i32.const 1))", "",
+ WebAssembly.CompileError,
+ /(popping value from empty stack)|(expected i32 but nothing on stack)/);
+
+// invalid argument types
+{
+ const tys = ['i32', 'f32', 'i64', 'f64'];
+
+ const ops = ['table.init 1', 'table.copy'];
+ for (let ty1 of tys) {
+ for (let ty2 of tys) {
+ for (let ty3 of tys) {
+ for (let op of ops) {
+ if (ty1 == 'i32' && ty2 == 'i32' && ty3 == 'i32')
+ continue; // this is the only valid case
+ let i1 = `(${op} (${ty1}.const 1) (${ty2}.const 1) (${ty3}.const 1))`;
+ tab_test(i1, "", WebAssembly.CompileError, /type mismatch/);
+ }}}}
+}
+
+
+//---- table.copy ---------------------------------------------------------
+
+// There are no immediates here, only 3 dynamic args. So we're limited to
+// runtime boundary checks.
+
+// passive-segs-smoketest.js tests the normal, non-exception cases of
+// table.copy. Here we just test the boundary-failure cases. The
+// table's valid indices are 0 .. 29 inclusive.
+
+// copy: dst range invalid
+tab_test("(table.copy (i32.const 28) (i32.const 1) (i32.const 3))",
+ "",
+ WebAssembly.RuntimeError, /index out of bounds/);
+
+// copy: dst wraparound end of 32 bit offset space
+tab_test("(table.copy (i32.const 0xFFFFFFFE) (i32.const 1) (i32.const 2))",
+ "",
+ WebAssembly.RuntimeError, /index out of bounds/);
+
+// copy: src range invalid
+tab_test("(table.copy (i32.const 15) (i32.const 25) (i32.const 6))",
+ "",
+ WebAssembly.RuntimeError, /index out of bounds/);
+
+// copy: src wraparound end of 32 bit offset space
+tab_test("(table.copy (i32.const 15) (i32.const 0xFFFFFFFE) (i32.const 2))",
+ "",
+ WebAssembly.RuntimeError, /index out of bounds/);
+
+// copy: zero length with both offsets in-bounds is OK
+tab_test_nofail(
+ "(table.copy (i32.const 15) (i32.const 25) (i32.const 0))",
+ "");
+
+// copy: zero length with dst offset out of bounds at the edge
+tab_test("(table.copy (i32.const 30) (i32.const 15) (i32.const 0))",
+ "");
+
+// copy: zero length with dst offset out of bounds one past the edge
+tab_test("(table.copy (i32.const 31) (i32.const 15) (i32.const 0))",
+ "", WebAssembly.RuntimeError, /index out of bounds/);
+
+// copy: zero length with src offset out of bounds at the edge
+tab_test("(table.copy (i32.const 15) (i32.const 30) (i32.const 0))",
+ "");
+
+// copy: zero length with src offset out of bounds one past the edge
+tab_test("(table.copy (i32.const 15) (i32.const 31) (i32.const 0))",
+ "", WebAssembly.RuntimeError, /index out of bounds/);