memory.js (20734B)
1 const RuntimeError = WebAssembly.RuntimeError; 2 3 function loadModuleSrc(type, ext, offset, align, drop = false) { 4 let maybeResult = drop ? '' : `(result ${type})`; 5 let maybeDrop = drop ? 'drop' : ''; 6 return `(module 7 (memory 1) 8 (data (i32.const 0) "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f") 9 (data (i32.const 16) "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff") 10 (func $load (param i32) ${maybeResult} 11 (${type}.load${ext} 12 offset=${offset} 13 ${align != 0 ? 'align=' + align : ''} 14 (local.get 0) 15 ) 16 ${maybeDrop} 17 ) (export "" (func 0)))`; 18 } 19 function loadModule(type, ext, offset, align, drop = false) { 20 return wasmEvalText(loadModuleSrc(type, ext, offset, align, drop)).exports[""]; 21 } 22 23 function storeModuleSrc(type, ext, offset, align) { 24 var load_ext = ext === '' ? '' : ext + '_s'; 25 return `(module 26 (memory 1) 27 (data (i32.const 0) "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f") 28 (data (i32.const 16) "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff") 29 (func $store (param i32) (param ${type}) 30 (${type}.store${ext} 31 offset=${offset} 32 ${align != 0 ? 'align=' + align : ''} 33 (local.get 0) 34 (local.get 1) 35 ) 36 ) (export "store" (func 0)) 37 (func $load (param i32) (result ${type}) 38 (${type}.load${load_ext} 39 offset=${offset} 40 ${align != 0 ? 'align=' + align : ''} 41 (local.get 0) 42 ) 43 ) (export "load" (func 1)))`; 44 } 45 function storeModule(type, ext, offset, align) { 46 return wasmEvalText(storeModuleSrc(type, ext, offset, align)).exports; 47 } 48 49 function storeModuleCstSrc(type, ext, offset, align, value) { 50 var load_ext = ext === '' ? '' : ext + '_s'; 51 return `(module 52 (memory 1) 53 (data (i32.const 0) "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f") 54 (data (i32.const 16) "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff") 55 (func $store (param i32) 56 (${type}.store${ext} 57 offset=${offset} 58 ${align != 0 ? 'align=' + align : ''} 59 (local.get 0) 60 (${type}.const ${value}) 61 ) 62 ) (export "store" (func 0)) 63 (func $load (param i32) (result ${type}) 64 (${type}.load${load_ext} 65 offset=${offset} 66 ${align != 0 ? 'align=' + align : ''} 67 (local.get 0) 68 ) 69 ) (export "load" (func 1)))`; 70 } 71 function storeModuleCst(type, ext, offset, align, value) { 72 return wasmEvalText(storeModuleCstSrc(type, ext, offset, align, value)).exports; 73 } 74 75 function testLoad(type, ext, base, offset, align, expect) { 76 if (type === 'i64') { 77 wasmAssert(loadModuleSrc(type, ext, offset, align), [{ 78 type, 79 func: '$load', 80 expected: expect, 81 args: [`i32.const ${base}`] 82 }]); 83 } 84 else { 85 assertEq(loadModule(type, ext, offset, align)(base), expect); 86 } 87 } 88 89 function testLoadOOB(type, ext, base, offset, align) { 90 assertErrorMessage(() => loadModule(type, ext, offset, align, /*drop*/ true)(base), RuntimeError, /index out of bounds/); 91 } 92 93 function testStore(type, ext, base, offset, align, value) { 94 if (type === 'i64') { 95 wasmAssert(storeModuleSrc(type, ext, offset, align), [ 96 {type, func: '$store', args: [`i32.const ${base}`, `i64.const ${value}`]}, 97 {type, func: '$load', args: [`i32.const ${base}`], expected: value}, 98 ]); 99 wasmAssert(storeModuleCstSrc(type, ext, offset, align, value), [ 100 {type, func: '$store', args: [`i32.const ${base}`]}, 101 {type, func: '$load', args: [`i32.const ${base}`], expected: value}, 102 ]); 103 } else { 104 let module = storeModule(type, ext, offset, align); 105 let moduleCst = storeModuleCst(type, ext, offset, align, value); 106 module.store(base, value); 107 assertEq(module.load(base), value); 108 moduleCst.store(base); 109 assertEq(moduleCst.load(base), value); 110 } 111 } 112 113 function testStoreOOB(type, ext, base, offset, align, value) { 114 if (type === 'i64') { 115 assertErrorMessage(() => wasmAssert( 116 storeModuleSrc(type, ext, offset, align), 117 [{type, func: '$store', args: [`i32.const ${base}`, `i64.const ${value}`]}] 118 ), RuntimeError, /index out of bounds/); 119 } else { 120 assertErrorMessage(() => storeModule(type, ext, offset, align).store(base, value), RuntimeError, /index out of bounds/); 121 } 122 } 123 124 function badLoadModule(type, ext) { 125 wasmFailValidateText( `(module (func (param i32) (${type}.load${ext} (local.get 0))) (export "" (func 0)))`, /memory index/); 126 } 127 128 function badStoreModule(type, ext) { 129 wasmFailValidateText(`(module (func (param i32) (${type}.store${ext} (local.get 0) (${type}.const 0))) (export "" (func 0)))`, /memory index/); 130 } 131 132 // Can't touch memory. 133 for (let [type, ext] of [ 134 ['i32', ''], 135 ['i32', '8_s'], 136 ['i32', '8_u'], 137 ['i32', '16_s'], 138 ['i32', '16_u'], 139 ['i64', ''], 140 ['i64', '8_s'], 141 ['i64', '8_u'], 142 ['i64', '16_s'], 143 ['i64', '16_u'], 144 ['i64', '32_s'], 145 ['i64', '32_u'], 146 ['f32', ''], 147 ['f64', ''], 148 ]) 149 { 150 badLoadModule(type, ext); 151 } 152 153 for (let [type, ext] of [ 154 ['i32', ''], 155 ['i32', '8'], 156 ['i32', '16'], 157 ['i64', ''], 158 ['i64', '8'], 159 ['i64', '16'], 160 ['i64', '32'], 161 ['f32', ''], 162 ['f64', ''], 163 ]) 164 { 165 badStoreModule(type, ext); 166 } 167 168 assertEq(getJitCompilerOptions()['wasm.fold-offsets'], 1); 169 170 for (var foldOffsets = 0; foldOffsets <= 1; foldOffsets++) { 171 setJitCompilerOption('wasm.fold-offsets', foldOffsets | 0); 172 173 testLoad('i32', '', 0, 0, 0, 0x03020100); 174 testLoad('i32', '', 1, 0, 1, 0x04030201); 175 testLoad('i32', '', 0, 4, 0, 0x07060504); 176 testLoad('i32', '', 1, 3, 4, 0x07060504); 177 testLoad('f32', '', 0, 0, 0, 3.820471434542632e-37); 178 testLoad('f32', '', 1, 0, 1, 1.539989614439558e-36); 179 testLoad('f32', '', 0, 4, 0, 1.0082513512365273e-34); 180 testLoad('f32', '', 1, 3, 4, 1.0082513512365273e-34); 181 testLoad('f64', '', 0, 0, 0, 7.949928895127363e-275); 182 testLoad('f64', '', 1, 0, 1, 5.447603722011605e-270); 183 testLoad('f64', '', 0, 8, 0, 3.6919162048650923e-236); 184 testLoad('f64', '', 1, 7, 8, 3.6919162048650923e-236); 185 186 testLoad('i32', '8_s', 16, 0, 0, -0x10); 187 testLoad('i32', '8_u', 16, 0, 0, 0xf0); 188 testLoad('i32', '16_s', 16, 0, 0, -0xe10); 189 testLoad('i32', '16_u', 16, 0, 0, 0xf1f0); 190 191 testStore('i32', '', 0, 0, 0, -0x3f3e2c2c); 192 testStore('i32', '', 1, 0, 1, -0x3f3e2c2c); 193 testStore('i32', '', 0, 1, 1, -0x3f3e2c2c); 194 testStore('i32', '', 1, 1, 4, -0x3f3e2c2c); 195 196 testStore('f32', '', 0, 0, 0, 0.01234566979110241); 197 testStore('f32', '', 1, 0, 1, 0.01234566979110241); 198 testStore('f32', '', 0, 4, 0, 0.01234566979110241); 199 testStore('f32', '', 1, 3, 4, 0.01234566979110241); 200 testStore('f64', '', 0, 0, 0, 0.89012345); 201 testStore('f64', '', 1, 0, 1, 0.89012345); 202 testStore('f64', '', 0, 8, 0, 0.89012345); 203 testStore('f64', '', 1, 7, 8, 0.89012345); 204 205 testStore('i32', '8', 0, 0, 0, 0x23); 206 testStore('i32', '16', 0, 0, 0, 0x2345); 207 208 wasmFailValidateText('(module (memory 2 1))', /maximum length 1 is less than initial length 2/); 209 210 // Test bounds checks and edge cases. 211 212 for (let align of [0,1,2,4]) { 213 214 for (let offset of [0, 1, 2, 3, 4, 8, 16, 41, 0xfff8]) { 215 // Accesses of 1 byte. 216 let lastValidIndex = 0x10000 - 1 - offset; 217 218 if (align < 2) { 219 testLoad('i32', '8_s', lastValidIndex, offset, align, 0); 220 testLoadOOB('i32', '8_s', lastValidIndex + 1, offset, align); 221 222 testLoad('i32', '8_u', lastValidIndex, offset, align, 0); 223 testLoadOOB('i32', '8_u', lastValidIndex + 1, offset, align); 224 225 testStore('i32', '8', lastValidIndex, offset, align, -42); 226 testStoreOOB('i32', '8', lastValidIndex + 1, offset, align, -42); 227 } 228 229 // Accesses of 2 bytes. 230 lastValidIndex = 0x10000 - 2 - offset; 231 232 if (align < 4) { 233 testLoad('i32', '16_s', lastValidIndex, offset, align, 0); 234 testLoadOOB('i32', '16_s', lastValidIndex + 1, offset, align); 235 236 testLoad('i32', '16_u', lastValidIndex, offset, align, 0); 237 testLoadOOB('i32', '16_u', lastValidIndex + 1, offset, align); 238 239 testStore('i32', '16', lastValidIndex, offset, align, -32768); 240 testStoreOOB('i32', '16', lastValidIndex + 1, offset, align, -32768); 241 } 242 243 // Accesses of 4 bytes. 244 lastValidIndex = 0x10000 - 4 - offset; 245 246 testLoad('i32', '', lastValidIndex, offset, align, 0); 247 testLoadOOB('i32', '', lastValidIndex + 1, offset, align); 248 249 testLoad('f32', '', lastValidIndex, offset, align, 0); 250 testLoadOOB('f32', '', lastValidIndex + 1, offset, align); 251 252 testStore('i32', '', lastValidIndex, offset, align, 1337); 253 testStoreOOB('i32', '', lastValidIndex + 1, offset, align, 1337); 254 255 testStore('f32', '', lastValidIndex, offset, align, Math.fround(13.37)); 256 testStoreOOB('f32', '', lastValidIndex + 1, offset, align, Math.fround(13.37)); 257 258 // Accesses of 8 bytes. 259 lastValidIndex = 0x10000 - 8 - offset; 260 261 testLoad('f64', '', lastValidIndex, offset, align, 0); 262 testLoadOOB('f64', '', lastValidIndex + 1, offset, align); 263 264 testStore('f64', '', lastValidIndex, offset, align, 1.23456789); 265 testStoreOOB('f64', '', lastValidIndex + 1, offset, align, 1.23456789); 266 } 267 268 // Ensure wrapping doesn't apply. 269 offset = 0x7fffffff; 270 for (let index of [0, 1, 2, 3, 0x7fffffff, 0x80000000, 0x80000001]) { 271 if (align < 2) { 272 testLoadOOB('i32', '8_s', index, offset, align); 273 } 274 if (align < 4) { 275 testLoadOOB('i32', '16_s', index, offset, align); 276 } 277 testLoadOOB('i32', '', index, offset, align); 278 testLoadOOB('f32', '', index, offset, align); 279 testLoadOOB('f64', '', index, offset, align); 280 } 281 282 // Ensure out of bounds when the offset is greater than the immediate range. 283 index = 0; 284 for (let offset of [0x80000000, 0xfffffffe, 0xffffffff]) { 285 testLoadOOB('i32', '8_s', index, offset, 1); 286 testLoadOOB('i32', '16_s', index, offset, 1); 287 testLoadOOB('i32', '16_s', index, offset, 2); 288 testLoadOOB('i32', '', index, offset, 1); 289 testLoadOOB('i32', '', index, offset, 4); 290 testLoadOOB('f32', '', index, offset, 1); 291 testLoadOOB('f32', '', index, offset, 4); 292 testLoadOOB('f64', '', index, offset, 1); 293 testLoadOOB('f64', '', index, offset, 8); 294 } 295 296 wasmFailValidateText('(module (memory 1) (func (f64.store offset=0 (i32.const 0) (i32.const 0))))', mismatchError("i32", "f64")); 297 wasmFailValidateText('(module (memory 1) (func (f64.store offset=0 (i32.const 0) (f32.const 0))))', mismatchError("f32", "f64")); 298 299 wasmFailValidateText('(module (memory 1) (func (f32.store offset=0 (i32.const 0) (i32.const 0))))', mismatchError("i32", "f32")); 300 wasmFailValidateText('(module (memory 1) (func (f32.store offset=0 (i32.const 0) (f64.const 0))))', mismatchError("f64", "f32")); 301 302 wasmFailValidateText('(module (memory 1) (func (i32.store offset=0 (i32.const 0) (f32.const 0))))', mismatchError("f32", "i32")); 303 wasmFailValidateText('(module (memory 1) (func (i32.store offset=0 (i32.const 0) (f64.const 0))))', mismatchError("f64", "i32")); 304 305 // Test high number of registers. 306 function testRegisters() { 307 assertEq(wasmEvalText( 308 `(module 309 (memory 1) 310 (data (i32.const 0) "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f") 311 (data (i32.const 16) "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff") 312 (func (param i32) (result i32) (local i32 i32 i32 i32 f32 f64) 313 (local.set 1 (i32.load8_s offset=4 (local.get 0))) 314 (local.set 2 (i32.load16_s (local.get 1))) 315 (i32.store8 offset=4 (local.get 0) (local.get 1)) 316 (local.set 3 (i32.load16_u (local.get 2))) 317 (i32.store16 (local.get 1) (local.get 2)) 318 (local.set 4 (i32.load (local.get 2))) 319 (i32.store (local.get 1) (local.get 2)) 320 (local.set 5 (f32.load (local.get 4))) 321 (f32.store (local.get 4) (local.get 5)) 322 (local.set 6 (f64.load (local.get 4))) 323 (f64.store (local.get 4) (local.get 6)) 324 (i32.add 325 (i32.add 326 (local.get 0) 327 (local.get 1) 328 ) 329 (i32.add 330 (i32.add 331 (local.get 2) 332 (local.get 3) 333 ) 334 (i32.add 335 (local.get 4) 336 (i32.reinterpret_f32 (local.get 5)) 337 ) 338 ) 339 ) 340 ) (export "" (func 0)))` 341 ).exports[""](1), 50464523); 342 } 343 344 testRegisters(); 345 346 testLoad('i64', '', 0, 0, 0, '0x0706050403020100'); 347 testLoad('i64', '', 1, 0, 0, '0x0807060504030201'); 348 testLoad('i64', '', 0, 1, 0, '0x0807060504030201'); 349 testLoad('i64', '', 1, 1, 4, '0x0908070605040302'); 350 351 testLoad('i64', '8_s', 16, 0, 0, -0x10); 352 testLoad('i64', '8_u', 16, 0, 0, 0xf0); 353 testLoad('i64', '16_s', 16, 0, 0, -0xe10); 354 testLoad('i64', '16_u', 16, 0, 0, 0xf1f0); 355 testLoad('i64', '32_s', 16, 0, 0, 0xf3f2f1f0 | 0); 356 testLoad('i64', '32_u', 16, 0, 0, '0xf3f2f1f0'); 357 358 testStore('i64', '', 0, 0, 0, '0xc0c1d3d4e6e7090a'); 359 testStore('i64', '', 1, 0, 0, '0xc0c1d3d4e6e7090a'); 360 testStore('i64', '', 0, 1, 0, '0xc0c1d3d4e6e7090a'); 361 testStore('i64', '', 1, 1, 4, '0xc0c1d3d4e6e7090a'); 362 testStore('i64', '8', 0, 0, 0, 0x23); 363 testStore('i64', '16', 0, 0, 0, 0x23); 364 testStore('i64', '32', 0, 0, 0, 0x23); 365 366 for (let offset of [0, 1, 2, 3, 4, 8, 16, 41, 0xfff8]) { 367 // Accesses of 1 byte. 368 let lastValidIndex = 0x10000 - 1 - offset; 369 370 if (align < 2) { 371 testLoad('i64', '8_s', lastValidIndex, offset, align, 0); 372 testLoadOOB('i64', '8_s', lastValidIndex + 1, offset, align); 373 374 testLoad('i64', '8_u', lastValidIndex, offset, align, 0); 375 testLoadOOB('i64', '8_u', lastValidIndex + 1, offset, align); 376 377 testStore('i64', '8', lastValidIndex, offset, align, -42); 378 testStoreOOB('i64', '8', lastValidIndex + 1, offset, align, -42); 379 } 380 381 // Accesses of 2 bytes. 382 lastValidIndex = 0x10000 - 2 - offset; 383 384 if (align < 4) { 385 testLoad('i64', '16_s', lastValidIndex, offset, align, 0); 386 testLoadOOB('i64', '16_s', lastValidIndex + 1, offset, align); 387 388 testLoad('i64', '16_u', lastValidIndex, offset, align, 0); 389 testLoadOOB('i64', '16_u', lastValidIndex + 1, offset, align); 390 391 testStore('i64', '16', lastValidIndex, offset, align, -32768); 392 testStoreOOB('i64', '16', lastValidIndex + 1, offset, align, -32768); 393 } 394 395 // Accesses of 4 bytes. 396 lastValidIndex = 0x10000 - 4 - offset; 397 398 testLoad('i64', '32_s', lastValidIndex, offset, align, 0); 399 testLoadOOB('i64', '32_s', lastValidIndex + 1, offset, align); 400 401 testLoad('i64', '32_u', lastValidIndex, offset, align, 0); 402 testLoadOOB('i64', '32_u', lastValidIndex + 1, offset, align); 403 404 testStore('i64', '32', lastValidIndex, offset, align, 0xf1231337 | 0); 405 testStoreOOB('i64', '32', lastValidIndex + 1, offset, align, 0xf1231337 | 0); 406 407 // Accesses of 8 bytes. 408 lastValidIndex = 0x10000 - 8 - offset; 409 410 testLoad('i64', '', lastValidIndex, offset, align, 0); 411 testLoadOOB('i64', '', lastValidIndex + 1, offset, align); 412 413 testStore('i64', '', lastValidIndex, offset, align, '0x1234567887654321'); 414 testStoreOOB('i64', '', lastValidIndex + 1, offset, align, '0x1234567887654321'); 415 } 416 } 417 } 418 419 setJitCompilerOption('wasm.fold-offsets', 1); 420 421 // Test active segments with a memory index. 422 423 { 424 function makeIt(flag, memindex) { 425 return new Uint8Array([0x00, 0x61, 0x73, 0x6d, 426 0x01, 0x00, 0x00, 0x00, 427 0x05, // Memory section 428 0x03, // Section size 429 0x01, // One memory 430 0x00, // Unshared, min only 431 0x01, // Min 432 0x0b, // Data section 433 0x0a, // Section size 434 0x01, // One data segment 435 flag, // Flag should be 2, or > 2 if invalid 436 memindex, // Memory index should be 0, or > 0 if invalid 437 0x41, // Init expr: i32.const 438 0x00, // Init expr: zero (payload) 439 0x0b, // Init expr: end 440 0x03, // Three bytes, because why not? 441 0x01, 442 0x02, 443 0x03]); 444 } 445 446 // Should succeed because this is what an active segment with index looks like 447 new WebAssembly.Module(makeIt(0x02, 0x00)); 448 449 // Should fail because the kind is unknown 450 assertErrorMessage(() => new WebAssembly.Module(makeIt(0x03, 0x00)), 451 WebAssembly.CompileError, 452 /invalid data initializer-kind/); 453 454 // Should fail because the memory index is bad 455 assertErrorMessage(() => new WebAssembly.Module(makeIt(0x02, 0x01)), 456 WebAssembly.CompileError, 457 /invalid memory index/); 458 } 459 460 // Misc syntax for data. 461 462 // When memory index is present it must be zero, and the offset must be present too; 463 // but it's OK for there to be neither 464 new WebAssembly.Module(wasmTextToBinary(`(module (memory 1) (data 0 (i32.const 0) ""))`)); 465 new WebAssembly.Module(wasmTextToBinary(`(module (memory 1) (data 0 (offset (i32.const 0)) ""))`)); 466 new WebAssembly.Module(wasmTextToBinary(`(module (memory 1) (data ""))`)); 467 assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(`(module (memory 1) (data 0 ""))`)), 468 SyntaxError, 469 /wasm text error/); 470 assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(`(module (memory 1) (data 1 (i32.const 0) ""))`)), 471 WebAssembly.CompileError, 472 /invalid memory index/); 473 474 475 // Make sure we handle memory instructions without memory 476 477 var nomem = /memory index out of range/; 478 479 assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(`(module (func (result i32) memory.size))`)), 480 WebAssembly.CompileError, 481 nomem); 482 483 assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(`(module (func (result i32) (memory.grow (i32.const 1))))`)), 484 WebAssembly.CompileError, 485 nomem); 486 487 assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(` 488 (module (func (param i32 i32 i32) 489 (memory.copy (local.get 0) (local.get 1) (local.get 2))))`)), 490 WebAssembly.CompileError, 491 nomem); 492 493 assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(` 494 (module (func (param i32 i32 i32) 495 (memory.fill (local.get 0) (local.get 1) (local.get 2))))`)), 496 WebAssembly.CompileError, 497 nomem); 498 499 assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(` 500 (module 501 (data $d "01234") 502 (func (param i32 i32) 503 (memory.init $d (local.get 0) (local.get 1))))`)), 504 WebAssembly.CompileError, 505 nomem);