| import Builder from '../Builder.js'; |
| import * as assert from '../assert.js'; |
| import * as LLB from '../LowLevelBinary.js'; |
| import * as WASM from '../WASM.js'; |
| import * as util from '../utilities.js'; |
| |
| const verbose = false; |
| |
| const pageSize = 64 * 1024; |
| const fourGiB = pageSize * 65536; |
| const initial = 64; |
| const maximum = 128; |
| |
| // When using fast memories, we allocate a redzone after the 4GiB huge |
| // allocation. This redzone is used to trap reg+imm accesses which exceed |
| // 32-bits. Anything past 4GiB must trap, but we cannot know statically that it |
| // will. |
| |
| const offsets = [ |
| 0, |
| 1, |
| 2, |
| 1024, |
| pageSize, |
| pageSize + pageSize / 2, |
| pageSize * 8, |
| pageSize * 16, |
| pageSize * 128, |
| pageSize * 512, |
| fourGiB / 4 - 4, |
| fourGiB / 4 - 3, |
| fourGiB / 4 - 2, |
| fourGiB / 4 - 1, |
| fourGiB / 4, |
| fourGiB / 4 + 1, |
| fourGiB / 4 + 2, |
| fourGiB / 4 + 3, |
| fourGiB / 4 + 4, |
| fourGiB / 2 - 4, |
| fourGiB / 2 - 3, |
| fourGiB / 2 - 2, |
| fourGiB / 2 - 1, |
| fourGiB / 2, |
| fourGiB / 2 + 1, |
| fourGiB / 2 + 2, |
| fourGiB / 2 + 3, |
| fourGiB / 2 + 4, |
| (fourGiB / 4) * 3, |
| fourGiB - 4, |
| fourGiB - 3, |
| fourGiB - 2, |
| fourGiB - 1, |
| ]; |
| |
| for (let memoryDeclaration of [{ initial: initial }, { initial: initial, maximum: maximum }]) { |
| fullGC(); |
| |
| // Re-use a single memory so tests are more likely to get a fast memory. |
| const memory = new WebAssembly.Memory(memoryDeclaration); |
| if (verbose) |
| print(WebAssemblyMemoryMode(memory)); |
| const buf = new Uint8Array(memory.buffer); |
| |
| // Enumerate all memory access types. |
| for (const op of WASM.opcodes("memory")) { |
| const info = WASM.memoryAccessInfo(op); |
| |
| // The accesses should fault even if only the last byte is off the end. |
| let wiggles = [0]; |
| for (let wiggle = 0; wiggle !== info.width / 8; ++wiggle) |
| wiggles.push(wiggle); |
| |
| let builder = (new Builder()) |
| .Type().End() |
| .Import().Memory("imp", "memory", memoryDeclaration).End() |
| .Function().End() |
| .Export(); |
| |
| for (let offset of offsets) |
| switch (info.type) { |
| case "load": builder = builder.Function("get_" + offset); break; |
| case "store": builder = builder.Function("set_" + offset); break; |
| default: throw new Error(`Implementation problem: unknown memory access type ${info.type}`); |
| } |
| |
| builder = builder.End().Code(); |
| |
| const align = 0; // No need to be precise, it's just a hint. |
| const constInstr = util.toJavaScriptName(WASM.constForValueType(info.valueType)); |
| const instr = util.toJavaScriptName(op.name); |
| for (let offset of offsets) |
| switch (info.type) { |
| case "load": |
| builder = builder.Function("get_" + offset, { params: ["i32"] }).GetLocal(0)[instr](align, offset).Drop().End(); |
| break; |
| case "store": |
| builder = builder.Function("set_" + offset, { params: ["i32"] }).GetLocal(0)[constInstr](0xdead)[instr](align, offset).End(); |
| break; |
| default: |
| throw new Error(`Implementation problem: unknown memory access type ${info.type}`); |
| } |
| |
| builder = builder.End(); |
| |
| const instance = new WebAssembly.Instance(new WebAssembly.Module(builder.WebAssembly().get()), { imp: { memory: memory } }); |
| |
| for (let offset of offsets) { |
| for (let wiggle of wiggles) { |
| const address = LLB.varuint32Max - offset - wiggle; |
| if (verbose) |
| print(`${op.name.padStart(16, ' ')}: base address ${address > 0 ? '0x' : ' '}${address.toString(16).padStart(8, address > 0 ? '0' : ' ')} + offset 0x${offset.toString(16).padStart(8, '0')} - wiggle ${wiggle} = effective address 0x${(address + offset - wiggle).toString(16).padStart(16, '0')}`); |
| switch (info.type) { |
| case "load": |
| assert.throws(() => instance.exports["get_" + offset](address), WebAssembly.RuntimeError, `Out of bounds memory access`); |
| break; |
| case "store": |
| assert.throws(() => instance.exports["set_" + offset](address), WebAssembly.RuntimeError, `Out of bounds memory access`); |
| break; |
| default: throw new Error(`Implementation problem: unknown memory access type ${info.type}`); |
| } |
| } |
| } |
| |
| fullGC(); |
| } |
| |
| // Only check that the memory was untouched at the very end, before throwing it away entirely. |
| for (let idx = 0; idx < buf.byteLength; ++idx) |
| assert.eq(buf[idx], 0); |
| } |