blob: 1d3494ddd2f86e2a5252e7902955a9434aea9b8a [file] [log] [blame]
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);
}