v8/test/mjsunit/wasm/module-memory.js
Eric Holk b984b70c3a [wasm] Fall back on bounds checks when guarded memory unavailable
This CL adds the simplest version of a trap handler fallback. At
instantiation time, we check whether the module was compiled to use
trap handlers and the memory is guarded. If the memory is not guarded
but the module is supposed to use trap handlers, we recompile the
module with bounds checks so that we can use an unguarded memory.

The compiled module is replaced with a bounds checking version, meaning
future instances from this module will also use bounds checks.

Some likely desirable features that are current missing but can be
added future CLs include:
* Disabling trap handler mode entirely.
* Recompiling all old instances so that trap handler and bounds checked
  code does not coexist in the same process.

Bug: v8:7143

Change-Id: I161fc0d544133b07dc4a93cc6af813369aaf3efe
Reviewed-on: https://chromium-review.googlesource.com/1018182
Commit-Queue: Eric Holk <eholk@chromium.org>
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53566}
2018-06-07 01:29:53 +00:00

204 lines
5.8 KiB
JavaScript

// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-wasm --expose-gc --stress-compaction --allow-natives-syntax
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
var kMemSize = 65536;
function genModule(memory) {
var builder = new WasmModuleBuilder();
builder.addImportedMemory("", "memory", 1);
builder.exportMemoryAs("memory");
builder.addFunction("main", kSig_i_i)
.addBody([
// main body: while(i) { if(mem[i]) return -1; i -= 4; } return 0;
// TODO(titzer): this manual bytecode has a copy of test-run-wasm.cc
/**/ kExprLoop, kWasmStmt, // --
/* */ kExprGetLocal, 0, // --
/* */ kExprIf, kWasmStmt, // --
/* */ kExprGetLocal, 0, // --
/* */ kExprI32LoadMem, 0, 0, // --
/* */ kExprIf, kWasmStmt, // --
/* */ kExprI32Const, 127, // --
/* */ kExprReturn, // --
/* */ kExprEnd, // --
/* */ kExprGetLocal, 0, // --
/* */ kExprI32Const, 4, // --
/* */ kExprI32Sub, // --
/* */ kExprSetLocal, 0, // --
/* */ kExprBr, 1, // --
/* */ kExprEnd, // --
/* */ kExprEnd, // --
/**/ kExprI32Const, 0 // --
])
.exportFunc();
var module = builder.instantiate({"": {memory:memory}});
assertTrue(module.exports.memory instanceof WebAssembly.Memory);
if (memory != null) assertEquals(memory.buffer, module.exports.memory.buffer);
return module;
}
function testPokeMemory() {
print("testPokeMemory");
var module = genModule(new WebAssembly.Memory({initial: 1}));
var buffer = module.exports.memory.buffer;
var main = module.exports.main;
assertEquals(kMemSize, buffer.byteLength);
var array = new Int8Array(buffer);
assertEquals(kMemSize, array.length);
for (var i = 0; i < kMemSize; i++) {
assertEquals(0, array[i]);
}
for (var i = 0; i < 10; i++) {
assertEquals(0, main(kMemSize - 4));
array[kMemSize/2 + i] = 1;
assertEquals(0, main(kMemSize/2 - 4));
assertEquals(-1, main(kMemSize - 4));
array[kMemSize/2 + i] = 0;
assertEquals(0, main(kMemSize - 4));
}
}
testPokeMemory();
function genAndGetMain(buffer) {
return genModule(buffer).exports.main; // to prevent intermediates living
}
function testSurvivalAcrossGc() {
var checker = genAndGetMain(new WebAssembly.Memory({initial: 1}));
for (var i = 0; i < 3; i++) {
print("gc run ", i);
assertEquals(0, checker(kMemSize - 4));
gc();
}
}
testSurvivalAcrossGc();
testSurvivalAcrossGc();
testSurvivalAcrossGc();
testSurvivalAcrossGc();
function testPokeOuterMemory() {
print("testPokeOuterMemory");
var buffer = new WebAssembly.Memory({initial: kMemSize / kPageSize});
var module = genModule(buffer);
var main = module.exports.main;
assertEquals(kMemSize, buffer.buffer.byteLength);
var array = new Int8Array(buffer.buffer);
assertEquals(kMemSize, array.length);
for (var i = 0; i < kMemSize; i++) {
assertEquals(0, array[i]);
}
for (var i = 0; i < 10; i++) {
assertEquals(0, main(kMemSize - 4));
array[kMemSize/2 + i] = 1;
assertEquals(0, main(kMemSize/2 - 4));
assertEquals(-1, main(kMemSize - 4));
array[kMemSize/2 + i] = 0;
assertEquals(0, main(kMemSize - 4));
}
}
testPokeOuterMemory();
function testOuterMemorySurvivalAcrossGc() {
var buffer = new WebAssembly.Memory({initial: kMemSize / kPageSize});
var checker = genAndGetMain(buffer);
for (var i = 0; i < 3; i++) {
print("gc run ", i);
assertEquals(0, checker(kMemSize - 4));
gc();
}
}
testOuterMemorySurvivalAcrossGc();
testOuterMemorySurvivalAcrossGc();
testOuterMemorySurvivalAcrossGc();
testOuterMemorySurvivalAcrossGc();
function testOOBThrows() {
var builder = new WasmModuleBuilder();
builder.addMemory(1, 1, true);
builder.addFunction("geti", kSig_i_ii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprI32LoadMem, 0, 0,
kExprI32StoreMem, 0, 0,
kExprGetLocal, 1,
kExprI32LoadMem, 0, 0,
])
.exportFunc();
var module = builder.instantiate();
var offset;
function read() { return module.exports.geti(0, offset); }
function write() { return module.exports.geti(offset, 0); }
for (offset = 0; offset < 65533; offset++) {
assertEquals(0, read());
assertEquals(0, write());
}
for (offset = 65534; offset < 66536; offset++) {
const trap_count = %GetWasmRecoveredTrapCount();
assertTraps(kTrapMemOutOfBounds, read);
assertTraps(kTrapMemOutOfBounds, write);
if (%IsWasmTrapHandlerEnabled()) {
assertEquals(trap_count + 2, %GetWasmRecoveredTrapCount());
}
}
}
testOOBThrows();
function testAddressSpaceLimit() {
// 1TiB + 4 GiB, see wasm-memory.h
const kMaxAddressSpace = 1 * 1024 * 1024 * 1024 * 1024
+ 4 * 1024 * 1024 * 1024;
const kAddressSpacePerMemory = 8 * 1024 * 1024 * 1024;
let last_memory;
try {
let memories = [];
let address_space = 0;
while (address_space <= kMaxAddressSpace + 1) {
last_memory = new WebAssembly.Memory({initial: 1})
memories.push(last_memory);
address_space += kAddressSpacePerMemory;
}
} catch (e) {
assertTrue(e instanceof RangeError);
return;
}
// If we get here it's because our fallback behavior is working. We may not
// be using the fallback, in which case we would have thrown a RangeError in
// the previous block.
assertTrue(!%WasmMemoryHasFullGuardRegion(last_memory));
}
if(%IsWasmTrapHandlerEnabled()) {
testAddressSpaceLimit();
}