passive-segs-boundary.js (17176B)
1 // Perform a test which, 2 // 3 // * if errKind is defined, is expected to fail with an exception 4 // characterised by errKind and errText. 5 // 6 // * if errKind is undefined, is expected to succeed, in which case errKind 7 // and errText are ignored. 8 // 9 // The function body will be [insn1, insn2] and is constructed according to 10 // four booleans: 11 // 12 // * isMem controls whether the module is constructed with memory or 13 // table initializers. 14 // 15 // * haveStorage determines whether there is actually a memory or table to 16 // work with. 17 // 18 // * haveInitA controls whether active initializers are added. 19 // 20 // * haveInitP controls whether passive initializers are added. 21 22 function do_test(insn1, insn2, errKind, errText, 23 isMem, haveStorage, haveInitA, haveInitP) 24 { 25 let preamble; 26 if (isMem) { 27 let mem_def = haveStorage ? "(memory 1 1)" : ""; 28 let mem_ia1 = `(data (i32.const 2) "\\03\\01\\04\\01")`; 29 let mem_ia2 = `(data (i32.const 12) "\\07\\05\\02\\03\\06")`; 30 let mem_ip1 = `(data "\\02\\07\\01\\08")`; 31 let mem_ip2 = `(data "\\05\\09\\02\\07\\06")`; 32 let mem_init = ``; 33 if (haveInitA && haveInitP) 34 mem_init = `${mem_ia1} ${mem_ip1} ${mem_ia2} ${mem_ip2}`; 35 else if (haveInitA && !haveInitP) mem_init = `${mem_ia1} ${mem_ia2}`; 36 else if (!haveInitA && haveInitP) mem_init = `${mem_ip1} ${mem_ip2}`; 37 preamble 38 = `;; -------- Memories -------- 39 ${mem_def} 40 ;; -------- Memory initialisers -------- 41 ${mem_init} 42 `; 43 } else { 44 let tab_def = haveStorage ? "(table 30 30 funcref)" : ""; 45 let tab_ia1 = `(elem (i32.const 2) 3 1 4 1)`; 46 let tab_ia2 = `(elem (i32.const 12) 7 5 2 3 6)`; 47 let tab_ip1 = `(elem func 2 7 1 8)`; 48 let tab_ip2 = `(elem func 5 9 2 7 6)`; 49 let tab_init = ``; 50 if (haveInitA && haveInitP) 51 tab_init = `${tab_ia1} ${tab_ip1} ${tab_ia2} ${tab_ip2}`; 52 else if (haveInitA && !haveInitP) tab_init = `${tab_ia1} ${tab_ia2}`; 53 else if (!haveInitA && haveInitP) tab_init = `${tab_ip1} ${tab_ip2}`; 54 preamble 55 = `;; -------- Tables -------- 56 ${tab_def} 57 ;; -------- Table initialisers -------- 58 ${tab_init} 59 ;; ------ Functions (0..9) referred by the table/esegs ------ 60 (func (result i32) (i32.const 0)) 61 (func (result i32) (i32.const 1)) 62 (func (result i32) (i32.const 2)) 63 (func (result i32) (i32.const 3)) 64 (func (result i32) (i32.const 4)) 65 (func (result i32) (i32.const 5)) 66 (func (result i32) (i32.const 6)) 67 (func (result i32) (i32.const 7)) 68 (func (result i32) (i32.const 8)) 69 (func (result i32) (i32.const 9)) 70 `; 71 } 72 73 let txt = "(module\n" + preamble + 74 `;; -------- testfn -------- 75 (func (export "testfn") 76 ${insn1} 77 ${insn2} 78 ) 79 )`; 80 81 if (!!errKind) { 82 assertErrorMessage( 83 () => { 84 let inst = wasmEvalText(txt); 85 inst.exports.testfn(); 86 }, 87 errKind, 88 errText 89 ); 90 } else { 91 let inst = wasmEvalText(txt); 92 assertEq(undefined, inst.exports.testfn()); 93 } 94 } 95 96 97 // Some handy specialisations of do_test(). 98 99 function mem_test(insn1, insn2, errKind, errText, 100 haveStorage=true, haveInitA=true, haveInitP=true) { 101 do_test(insn1, insn2, errKind, errText, 102 /*isMem=*/true, haveStorage, haveInitA, haveInitP); 103 } 104 105 function mem_test_nofail(insn1, insn2, 106 haveStorage=true, haveInitA=true, haveInitP=true) { 107 do_test(insn1, insn2, undefined, undefined, 108 /*isMem=*/true, haveStorage, haveInitA, haveInitP); 109 } 110 111 function tab_test(insn1, insn2, errKind, errText, 112 haveStorage=true, haveInitA=true, haveInitP=true) { 113 do_test(insn1, insn2, errKind, errText, 114 /*isMem=*/false, haveStorage, haveInitA, haveInitP); 115 } 116 117 function tab_test_nofail(insn1, insn2, 118 haveStorage=true, haveInitA=true, haveInitP=true) { 119 do_test(insn1, insn2, undefined, undefined, 120 /*isMem=*/false, haveStorage, haveInitA, haveInitP); 121 } 122 123 124 //---- memory.{drop,init,copy} ------------------------------------------------- 125 126 // The tested semantics for memory.drop, in the case where there's no memory, 127 // are as follows. table.drop is analogous. 128 // 129 // no memory, no data segments: 130 // drop with any args -> fail OOB 131 // [because there's nothing to drop] 132 // 133 // no memory, data segments, at least one of which is active: 134 // -> always fails, regardless of insns 135 // [because active segments implicitly reference memory 0] 136 // 137 // no memory, data segments, all of which are passive: 138 // drop, segment index is OOB -> fail OOB 139 // [because it refers to non existent segment] 140 // 141 // drop, segment index is IB -> OK 142 143 // drop with no memory and no data segments 144 mem_test("data.drop 0", "", 145 WebAssembly.CompileError, /(data.drop segment index out of range)|(unknown data segment 0)/, 146 /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/false); 147 148 // drop with no memory but with both passive and active segments, ix in range 149 // and refers to a passive segment 150 mem_test("data.drop 3", "", 151 WebAssembly.CompileError, 152 /active data segment requires a memory section/, 153 /*haveStorage=*/false, /*haveInitA=*/true, /*haveInitP=*/true); 154 155 // drop with no memory but with passive segments only, ix out of range 156 mem_test("data.drop 2", "", 157 WebAssembly.CompileError, /(data.drop segment index out of range)|(unknown data segment 2)/, 158 /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/true); 159 160 // drop with no memory but with passive segments only, ix in range 161 mem_test_nofail("data.drop 1", "", 162 /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/true); 163 164 165 // init with no memory and no data segs 166 mem_test("(memory.init 1 (i32.const 1234) (i32.const 1) (i32.const 1))", "", 167 WebAssembly.CompileError, /memory index/, 168 /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/false); 169 170 // drop with data seg ix out of range 171 mem_test("data.drop 4", "", 172 WebAssembly.CompileError, /(data.drop segment index out of range)|(unknown data segment 4)/); 173 174 // init with data seg ix out of range 175 mem_test("(memory.init 4 (i32.const 1234) (i32.const 1) (i32.const 1))", "", 176 WebAssembly.CompileError, /(memory.init segment index out of range)|(unknown data segment 4)/); 177 178 // drop with data seg ix indicating an active segment 179 mem_test("data.drop 2", ""); 180 181 // init with data seg ix indicating an active segment 182 mem_test("(memory.init 2 (i32.const 1234) (i32.const 1) (i32.const 1))", "", 183 WebAssembly.RuntimeError, /index out of bounds/); 184 185 // init, using a data seg ix more than once is OK 186 mem_test_nofail( 187 "(memory.init 1 (i32.const 1234) (i32.const 1) (i32.const 1))", 188 "(memory.init 1 (i32.const 4321) (i32.const 1) (i32.const 1))"); 189 190 // drop, then drop 191 mem_test("data.drop 1", 192 "data.drop 1"); 193 194 // drop, then init 195 mem_test("data.drop 1", 196 "(memory.init 1 (i32.const 1234) (i32.const 1) (i32.const 1))", 197 WebAssembly.RuntimeError, /index out of bounds/); 198 199 // init: seg ix is valid passive, but length to copy > len of seg 200 mem_test("", 201 "(memory.init 1 (i32.const 1234) (i32.const 0) (i32.const 5))", 202 WebAssembly.RuntimeError, /index out of bounds/); 203 204 // init: seg ix is valid passive, but implies copying beyond end of seg 205 mem_test("", 206 "(memory.init 1 (i32.const 1234) (i32.const 2) (i32.const 3))", 207 WebAssembly.RuntimeError, /index out of bounds/); 208 209 // init: seg ix is valid passive, but implies copying beyond end of dst 210 mem_test("", 211 "(memory.init 1 (i32.const 0xFFFE) (i32.const 1) (i32.const 3))", 212 WebAssembly.RuntimeError, /index out of bounds/); 213 214 // init: seg ix is valid passive, zero len, but src offset out of bounds at the 215 // edge 216 mem_test("", 217 "(memory.init 1 (i32.const 1234) (i32.const 4) (i32.const 0))"); 218 219 // init: seg ix is valid passive, zero len, but src offset out of bounds one 220 // past the edge 221 mem_test("", 222 "(memory.init 1 (i32.const 1234) (i32.const 5) (i32.const 0))", 223 WebAssembly.RuntimeError, /index out of bounds/); 224 225 // init: seg ix is valid passive, zero len, but dst offset out of bounds at the 226 // edge 227 mem_test("", 228 "(memory.init 1 (i32.const 0x10000) (i32.const 2) (i32.const 0))"); 229 230 // init: seg ix is valid passive, zero len, but dst offset out of bounds one 231 // past the edge 232 mem_test("", 233 "(memory.init 1 (i32.const 0x10001) (i32.const 2) (i32.const 0))", 234 WebAssembly.RuntimeError, /index out of bounds/); 235 236 // drop: too many args 237 mem_test("data.drop 1 (i32.const 42)", "", 238 WebAssembly.CompileError, 239 /(unused values not explicitly dropped by end of block)|(values remaining on stack at end of block)/); 240 241 // init: too many args 242 mem_test("(memory.init 1 (i32.const 1) (i32.const 1) (i32.const 1) (i32.const 1))", 243 "", 244 WebAssembly.CompileError, /(unused values)|(values remaining on stack at end of block)/); 245 246 // init: too few args 247 mem_test("(memory.init 1 (i32.const 1) (i32.const 1))", "", 248 WebAssembly.CompileError, 249 /(popping value from empty stack)|(expected i32 but nothing on stack)/); 250 251 // invalid argument types 252 { 253 const tys = ['i32', 'f32', 'i64', 'f64']; 254 255 for (let ty1 of tys) { 256 for (let ty2 of tys) { 257 for (let ty3 of tys) { 258 if (ty1 == 'i32' && ty2 == 'i32' && ty3 == 'i32') 259 continue; // this is the only valid case 260 let i1 = `(memory.init 1 (${ty1}.const 1) (${ty2}.const 1) (${ty3}.const 1))`; 261 mem_test(i1, "", WebAssembly.CompileError, /type mismatch/); 262 }}} 263 } 264 265 // 266 //---- table.{drop,init} -------------------------------------------------- 267 268 // drop with no tables and no elem segments 269 tab_test("elem.drop 0", "", 270 WebAssembly.CompileError, 271 /(element segment index out of range for elem.drop)|(segment index out of bounds)/, 272 /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/false); 273 274 // drop with no tables but with both passive and active segments, ix in range 275 // and refers to a passive segment 276 tab_test("elem.drop 3", "", 277 WebAssembly.CompileError, 278 /active elem segment requires a table/, 279 /*haveStorage=*/false, /*haveInitA=*/true, /*haveInitP=*/true); 280 281 // drop with no tables but with passive segments only, ix out of range 282 tab_test("elem.drop 2", "", 283 WebAssembly.CompileError, 284 /(element segment index out of range for elem.drop)|(segment index out of bounds)/, 285 /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/true); 286 287 // drop with no tables but with passive segments only, ix in range 288 tab_test_nofail("elem.drop 1", "", 289 /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/true); 290 291 292 // init with no table 293 tab_test("(table.init 1 (i32.const 12) (i32.const 1) (i32.const 1))", "", 294 WebAssembly.CompileError, /(table index out of range)|(table index out of bounds)/, 295 /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/false); 296 297 // drop with elem seg ix out of range 298 tab_test("elem.drop 4", "", 299 WebAssembly.CompileError, /(element segment index out of range for elem.drop)|(segment index out of bounds)/); 300 301 // init with elem seg ix out of range 302 tab_test("(table.init 4 (i32.const 12) (i32.const 1) (i32.const 1))", "", 303 WebAssembly.CompileError, /(table.init segment index out of range)|(segment index out of bounds)/); 304 305 // drop with elem seg ix indicating an active segment 306 tab_test("elem.drop 2", ""); 307 308 // init with elem seg ix indicating an active segment 309 tab_test("(table.init 2 (i32.const 12) (i32.const 1) (i32.const 1))", "", 310 WebAssembly.RuntimeError, /index out of bounds/); 311 312 // init, using an elem seg ix more than once is OK 313 tab_test_nofail( 314 "(table.init 1 (i32.const 12) (i32.const 1) (i32.const 1))", 315 "(table.init 1 (i32.const 21) (i32.const 1) (i32.const 1))"); 316 317 // drop, then drop 318 tab_test("elem.drop 1", 319 "elem.drop 1"); 320 321 // drop, then init 322 tab_test("elem.drop 1", 323 "(table.init 1 (i32.const 12) (i32.const 1) (i32.const 1))", 324 WebAssembly.RuntimeError, /index out of bounds/); 325 326 // init: seg ix is valid passive, but length to copy > len of seg 327 tab_test("", 328 "(table.init 1 (i32.const 12) (i32.const 0) (i32.const 5))", 329 WebAssembly.RuntimeError, /index out of bounds/); 330 331 // init: seg ix is valid passive, but implies copying beyond end of seg 332 tab_test("", 333 "(table.init 1 (i32.const 12) (i32.const 2) (i32.const 3))", 334 WebAssembly.RuntimeError, /index out of bounds/); 335 336 // init: seg ix is valid passive, but implies copying beyond end of dst 337 tab_test("", 338 "(table.init 1 (i32.const 28) (i32.const 1) (i32.const 3))", 339 WebAssembly.RuntimeError, /index out of bounds/); 340 341 // init: seg ix is valid passive, zero len, but src offset out of bounds at the 342 // edge 343 tab_test("", 344 "(table.init 1 (i32.const 12) (i32.const 4) (i32.const 0))"); 345 346 // init: seg ix is valid passive, zero len, but src offset out of bounds one 347 // past the edge 348 tab_test("", 349 "(table.init 1 (i32.const 12) (i32.const 5) (i32.const 0))", 350 WebAssembly.RuntimeError, /index out of bounds/); 351 352 // init: seg ix is valid passive, zero len, but dst offset out of bounds 353 tab_test("", 354 "(table.init 1 (i32.const 30) (i32.const 2) (i32.const 0))"); 355 356 // init: seg ix is valid passive, zero len, but dst offset out of bounds one 357 // past the edge 358 tab_test("", 359 "(table.init 1 (i32.const 31) (i32.const 2) (i32.const 0))", 360 WebAssembly.RuntimeError, /index out of bounds/); 361 362 // drop: too many args 363 tab_test("elem.drop 1 (i32.const 42)", "", 364 WebAssembly.CompileError, 365 /(unused values not explicitly dropped by end of block)|(values remaining on stack at end of block)/); 366 367 // init: too many args 368 tab_test("(table.init 1 (i32.const 1) (i32.const 1) (i32.const 1) (i32.const 1))", 369 "", 370 WebAssembly.CompileError, /(unused values)|(values remaining on stack at end of block)/); 371 372 // init: too few args 373 tab_test("(table.init 1 (i32.const 1) (i32.const 1))", "", 374 WebAssembly.CompileError, 375 /(popping value from empty stack)|(expected i32 but nothing on stack)/); 376 377 // invalid argument types 378 { 379 const tys = ['i32', 'f32', 'i64', 'f64']; 380 381 const ops = ['table.init 1', 'table.copy']; 382 for (let ty1 of tys) { 383 for (let ty2 of tys) { 384 for (let ty3 of tys) { 385 for (let op of ops) { 386 if (ty1 == 'i32' && ty2 == 'i32' && ty3 == 'i32') 387 continue; // this is the only valid case 388 let i1 = `(${op} (${ty1}.const 1) (${ty2}.const 1) (${ty3}.const 1))`; 389 tab_test(i1, "", WebAssembly.CompileError, /type mismatch/); 390 }}}} 391 } 392 393 394 //---- table.copy --------------------------------------------------------- 395 396 // There are no immediates here, only 3 dynamic args. So we're limited to 397 // runtime boundary checks. 398 399 // passive-segs-smoketest.js tests the normal, non-exception cases of 400 // table.copy. Here we just test the boundary-failure cases. The 401 // table's valid indices are 0 .. 29 inclusive. 402 403 // copy: dst range invalid 404 tab_test("(table.copy (i32.const 28) (i32.const 1) (i32.const 3))", 405 "", 406 WebAssembly.RuntimeError, /index out of bounds/); 407 408 // copy: dst wraparound end of 32 bit offset space 409 tab_test("(table.copy (i32.const 0xFFFFFFFE) (i32.const 1) (i32.const 2))", 410 "", 411 WebAssembly.RuntimeError, /index out of bounds/); 412 413 // copy: src range invalid 414 tab_test("(table.copy (i32.const 15) (i32.const 25) (i32.const 6))", 415 "", 416 WebAssembly.RuntimeError, /index out of bounds/); 417 418 // copy: src wraparound end of 32 bit offset space 419 tab_test("(table.copy (i32.const 15) (i32.const 0xFFFFFFFE) (i32.const 2))", 420 "", 421 WebAssembly.RuntimeError, /index out of bounds/); 422 423 // copy: zero length with both offsets in-bounds is OK 424 tab_test_nofail( 425 "(table.copy (i32.const 15) (i32.const 25) (i32.const 0))", 426 ""); 427 428 // copy: zero length with dst offset out of bounds at the edge 429 tab_test("(table.copy (i32.const 30) (i32.const 15) (i32.const 0))", 430 ""); 431 432 // copy: zero length with dst offset out of bounds one past the edge 433 tab_test("(table.copy (i32.const 31) (i32.const 15) (i32.const 0))", 434 "", WebAssembly.RuntimeError, /index out of bounds/); 435 436 // copy: zero length with src offset out of bounds at the edge 437 tab_test("(table.copy (i32.const 15) (i32.const 30) (i32.const 0))", 438 ""); 439 440 // copy: zero length with src offset out of bounds one past the edge 441 tab_test("(table.copy (i32.const 15) (i32.const 31) (i32.const 0))", 442 "", WebAssembly.RuntimeError, /index out of bounds/);