passive-segs-partial-table.js (6291B)
1 // Sundry test cases for the "partial write" bounds checking semantics. 2 3 // table.init: out of bounds of the table or the element segment, and should 4 // not perform the operation at all. 5 // 6 // Arithmetic overflow of tableoffset + len or of segmentoffset + len should not 7 // affect the behavior. 8 9 // Note, the length of the element segment is 16. 10 const tbl_init_len = 16; 11 12 function tbl_init(min, max, backup, write, segoffs=0) { 13 let ins = wasmEvalText( 14 `(module 15 (table (export "tbl") ${min} ${max} funcref) 16 (elem func $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8 $f9 $f10 $f11 $f12 $f13 $f14 $f15) 17 (func $f0 (export "f0")) 18 (func $f1 (export "f1")) 19 (func $f2 (export "f2")) 20 (func $f3 (export "f3")) 21 (func $f4 (export "f4")) 22 (func $f5 (export "f5")) 23 (func $f6 (export "f6")) 24 (func $f7 (export "f7")) 25 (func $f8 (export "f8")) 26 (func $f9 (export "f9")) 27 (func $f10 (export "f10")) 28 (func $f11 (export "f11")) 29 (func $f12 (export "f12")) 30 (func $f13 (export "f13")) 31 (func $f14 (export "f14")) 32 (func $f15 (export "f15")) 33 (func (export "run") (param $offs i32) (param $len i32) 34 (table.init 0 (local.get $offs) (i32.const ${segoffs}) (local.get $len))))`); 35 // A fill writing past the end of the table should throw *and* have filled 36 // all the way up to the end. 37 // 38 // A fill reading past the end of the segment should throw *and* have filled 39 // table with as much data as was available. 40 let offs = min - backup; 41 assertErrorMessage(() => ins.exports.run(offs, write), 42 WebAssembly.RuntimeError, 43 /index out of bounds/); 44 let tbl = ins.exports.tbl; 45 for (let i=0; i < min; i++) { 46 assertEq(tbl.get(i), null); 47 } 48 } 49 50 // We exceed the bounds of the table but not of the element segment 51 tbl_init(tbl_init_len*2, tbl_init_len*4, Math.floor(tbl_init_len/2), tbl_init_len); 52 tbl_init(tbl_init_len*2, tbl_init_len*4, Math.floor(tbl_init_len/2)-1, tbl_init_len); 53 54 // We exceed the bounds of the element segment but not the table 55 tbl_init(tbl_init_len*10, tbl_init_len*20, tbl_init_len*4, tbl_init_len*2); 56 tbl_init(tbl_init_len*10, tbl_init_len*20, tbl_init_len*4-1, tbl_init_len*2-1); 57 58 // We arithmetically overflow the table limit but not the segment limit 59 tbl_init(tbl_init_len*4, tbl_init_len*4, tbl_init_len, 0xFFFFFFF0); 60 61 // We arithmetically overflow the segment limit but not the table limit 62 tbl_init(tbl_init_len, tbl_init_len, tbl_init_len, 0xFFFFFFFC, Math.floor(tbl_init_len/2)); 63 64 // table.copy: out of bounds of the table for the source or target, and should 65 // perform the operation at all. Major cases: 66 // 67 // - non-overlapping regions 68 // - overlapping regions with src > dest 69 // - overlapping regions with src == dest 70 // - overlapping regions with src < dest 71 // - arithmetic overflow on src addresses 72 // - arithmetic overflow on target addresses 73 // 74 // for each of those, 75 // 76 // - src address oob 77 // - target address oob 78 // - both oob 79 // 80 // Note we do not test the multi-table case here because that is part of the 81 // reftypes proposal; tests are in the gc/ subdirectory. 82 83 const tbl_copy_len = 16; 84 85 function tbl_copy(min, max, srcOffs, targetOffs, len) { 86 let ins = wasmEvalText( 87 `(module 88 (table (export "tbl") ${min} ${max} funcref) 89 (func $f0 (export "f0")) 90 (func $f1 (export "f1")) 91 (func $f2 (export "f2")) 92 (func $f3 (export "f3")) 93 (func $f4 (export "f4")) 94 (func $f5 (export "f5")) 95 (func $f6 (export "f6")) 96 (func $f7 (export "f7")) 97 (func $f8 (export "f8")) 98 (func $f9 (export "f9")) 99 (func $f10 (export "f10")) 100 (func $f11 (export "f11")) 101 (func $f12 (export "f12")) 102 (func $f13 (export "f13")) 103 (func $f14 (export "f14")) 104 (func $f15 (export "f15")) 105 (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) 106 (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))))`); 107 108 let tbl = ins.exports.tbl; 109 110 let copyDown = srcOffs < targetOffs; 111 let targetAvail = tbl.length - targetOffs; 112 let srcAvail = tbl.length - srcOffs; 113 let srcLim = srcOffs + Math.min(len, targetAvail, srcAvail); 114 115 for (let i=srcOffs, j=0; i < srcLim; i++, j++) 116 tbl.set(i, ins.exports["f" + j]); 117 assertErrorMessage(() => ins.exports.run(targetOffs, srcOffs, len), 118 WebAssembly.RuntimeError, 119 /index out of bounds/); 120 121 for (var i=0, s=0; i < tbl.length; i++ ) { 122 if (i >= srcOffs && i < srcLim) { 123 assertEq(tbl.get(i), ins.exports["f" + (s++)]); 124 continue; 125 } 126 assertEq(tbl.get(i), null); 127 } 128 } 129 130 // OOB target address, nonoverlapping 131 tbl_copy(tbl_copy_len*2, tbl_copy_len*4, 0, Math.floor(1.5*tbl_copy_len), tbl_copy_len); 132 tbl_copy(tbl_copy_len*2, tbl_copy_len*4, 0, Math.floor(1.5*tbl_copy_len)-1, tbl_copy_len-1); 133 134 // OOB source address, nonoverlapping 135 tbl_copy(tbl_copy_len*2, tbl_copy_len*4, Math.floor(1.5*tbl_copy_len), 0, tbl_copy_len); 136 tbl_copy(tbl_copy_len*2, tbl_copy_len*4, Math.floor(1.5*tbl_copy_len)-1, 0, tbl_copy_len-1); 137 138 // OOB target address, overlapping, src < target 139 tbl_copy(tbl_copy_len*2, tbl_copy_len*4, tbl_copy_len-5, Math.floor(1.5*tbl_copy_len), tbl_copy_len); 140 141 // OOB source address, overlapping, target < src 142 tbl_copy(tbl_copy_len*2, tbl_copy_len*4, Math.floor(1.5*tbl_copy_len), tbl_copy_len-5, tbl_copy_len); 143 144 // OOB both, overlapping, including src == target 145 tbl_copy(tbl_copy_len*2, tbl_copy_len*4, tbl_copy_len+5, Math.floor(1.5*tbl_copy_len), tbl_copy_len); 146 tbl_copy(tbl_copy_len*2, tbl_copy_len*4, Math.floor(1.5*tbl_copy_len), tbl_copy_len+5, tbl_copy_len); 147 tbl_copy(tbl_copy_len*2, tbl_copy_len*4, tbl_copy_len+5, tbl_copy_len+5, tbl_copy_len); 148 149 // Arithmetic overflow on source address. 150 tbl_copy(tbl_copy_len*8, tbl_copy_len*8, tbl_copy_len*7, 0, 0xFFFFFFE0); 151 152 // Arithmetic overflow on target adddress is an overlapping case. 153 tbl_copy(tbl_copy_len*8, tbl_copy_len*8, 0, tbl_copy_len*7, 0xFFFFFFE0);