Source code

Revision control

Copy as Markdown

Other Tools

// Sundry test cases for the "partial write" bounds checking semantics.
// table.init: out of bounds of the table or the element segment, and should
// not perform the operation at all.
//
// Arithmetic overflow of tableoffset + len or of segmentoffset + len should not
// affect the behavior.
// Note, the length of the element segment is 16.
const tbl_init_len = 16;
function tbl_init(min, max, backup, write, segoffs=0) {
let ins = wasmEvalText(
`(module
(table (export "tbl") ${min} ${max} funcref)
(elem func $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8 $f9 $f10 $f11 $f12 $f13 $f14 $f15)
(func $f0 (export "f0"))
(func $f1 (export "f1"))
(func $f2 (export "f2"))
(func $f3 (export "f3"))
(func $f4 (export "f4"))
(func $f5 (export "f5"))
(func $f6 (export "f6"))
(func $f7 (export "f7"))
(func $f8 (export "f8"))
(func $f9 (export "f9"))
(func $f10 (export "f10"))
(func $f11 (export "f11"))
(func $f12 (export "f12"))
(func $f13 (export "f13"))
(func $f14 (export "f14"))
(func $f15 (export "f15"))
(func (export "run") (param $offs i32) (param $len i32)
(table.init 0 (local.get $offs) (i32.const ${segoffs}) (local.get $len))))`);
// A fill writing past the end of the table should throw *and* have filled
// all the way up to the end.
//
// A fill reading past the end of the segment should throw *and* have filled
// table with as much data as was available.
let offs = min - backup;
assertErrorMessage(() => ins.exports.run(offs, write),
WebAssembly.RuntimeError,
/index out of bounds/);
let tbl = ins.exports.tbl;
for (let i=0; i < min; i++) {
assertEq(tbl.get(i), null);
}
}
// We exceed the bounds of the table but not of the element segment
tbl_init(tbl_init_len*2, tbl_init_len*4, Math.floor(tbl_init_len/2), tbl_init_len);
tbl_init(tbl_init_len*2, tbl_init_len*4, Math.floor(tbl_init_len/2)-1, tbl_init_len);
// We exceed the bounds of the element segment but not the table
tbl_init(tbl_init_len*10, tbl_init_len*20, tbl_init_len*4, tbl_init_len*2);
tbl_init(tbl_init_len*10, tbl_init_len*20, tbl_init_len*4-1, tbl_init_len*2-1);
// We arithmetically overflow the table limit but not the segment limit
tbl_init(tbl_init_len*4, tbl_init_len*4, tbl_init_len, 0xFFFFFFF0);
// We arithmetically overflow the segment limit but not the table limit
tbl_init(tbl_init_len, tbl_init_len, tbl_init_len, 0xFFFFFFFC, Math.floor(tbl_init_len/2));
// table.copy: out of bounds of the table for the source or target, and should
// perform the operation at all. Major cases:
//
// - non-overlapping regions
// - overlapping regions with src > dest
// - overlapping regions with src == dest
// - overlapping regions with src < dest
// - arithmetic overflow on src addresses
// - arithmetic overflow on target addresses
//
// for each of those,
//
// - src address oob
// - target address oob
// - both oob
//
// Note we do not test the multi-table case here because that is part of the
// reftypes proposal; tests are in the gc/ subdirectory.
const tbl_copy_len = 16;
function tbl_copy(min, max, srcOffs, targetOffs, len) {
let ins = wasmEvalText(
`(module
(table (export "tbl") ${min} ${max} funcref)
(func $f0 (export "f0"))
(func $f1 (export "f1"))
(func $f2 (export "f2"))
(func $f3 (export "f3"))
(func $f4 (export "f4"))
(func $f5 (export "f5"))
(func $f6 (export "f6"))
(func $f7 (export "f7"))
(func $f8 (export "f8"))
(func $f9 (export "f9"))
(func $f10 (export "f10"))
(func $f11 (export "f11"))
(func $f12 (export "f12"))
(func $f13 (export "f13"))
(func $f14 (export "f14"))
(func $f15 (export "f15"))
(func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32)
(table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))))`);
let tbl = ins.exports.tbl;
let copyDown = srcOffs < targetOffs;
let targetAvail = tbl.length - targetOffs;
let srcAvail = tbl.length - srcOffs;
let srcLim = srcOffs + Math.min(len, targetAvail, srcAvail);
for (let i=srcOffs, j=0; i < srcLim; i++, j++)
tbl.set(i, ins.exports["f" + j]);
assertErrorMessage(() => ins.exports.run(targetOffs, srcOffs, len),
WebAssembly.RuntimeError,
/index out of bounds/);
for (var i=0, s=0; i < tbl.length; i++ ) {
if (i >= srcOffs && i < srcLim) {
assertEq(tbl.get(i), ins.exports["f" + (s++)]);
continue;
}
assertEq(tbl.get(i), null);
}
}
// OOB target address, nonoverlapping
tbl_copy(tbl_copy_len*2, tbl_copy_len*4, 0, Math.floor(1.5*tbl_copy_len), tbl_copy_len);
tbl_copy(tbl_copy_len*2, tbl_copy_len*4, 0, Math.floor(1.5*tbl_copy_len)-1, tbl_copy_len-1);
// OOB source address, nonoverlapping
tbl_copy(tbl_copy_len*2, tbl_copy_len*4, Math.floor(1.5*tbl_copy_len), 0, tbl_copy_len);
tbl_copy(tbl_copy_len*2, tbl_copy_len*4, Math.floor(1.5*tbl_copy_len)-1, 0, tbl_copy_len-1);
// OOB target address, overlapping, src < target
tbl_copy(tbl_copy_len*2, tbl_copy_len*4, tbl_copy_len-5, Math.floor(1.5*tbl_copy_len), tbl_copy_len);
// OOB source address, overlapping, target < src
tbl_copy(tbl_copy_len*2, tbl_copy_len*4, Math.floor(1.5*tbl_copy_len), tbl_copy_len-5, tbl_copy_len);
// OOB both, overlapping, including src == target
tbl_copy(tbl_copy_len*2, tbl_copy_len*4, tbl_copy_len+5, Math.floor(1.5*tbl_copy_len), tbl_copy_len);
tbl_copy(tbl_copy_len*2, tbl_copy_len*4, Math.floor(1.5*tbl_copy_len), tbl_copy_len+5, tbl_copy_len);
tbl_copy(tbl_copy_len*2, tbl_copy_len*4, tbl_copy_len+5, tbl_copy_len+5, tbl_copy_len);
// Arithmetic overflow on source address.
tbl_copy(tbl_copy_len*8, tbl_copy_len*8, tbl_copy_len*7, 0, 0xFFFFFFE0);
// Arithmetic overflow on target adddress is an overlapping case.
tbl_copy(tbl_copy_len*8, tbl_copy_len*8, 0, tbl_copy_len*7, 0xFFFFFFE0);