[wasm] Fix and tighten memory validation

Makes us pass the spec's memory.wast test.

R=titzer@chromium.org
BUG=

Review-Url: https://codereview.chromium.org/2640453003
Cr-Commit-Position: refs/heads/master@{#42452}
This commit is contained in:
rossberg 2017-01-18 04:07:57 -08:00 committed by Commit bot
parent 3c89788373
commit b86ef5ce8a
13 changed files with 138 additions and 63 deletions

View File

@ -622,6 +622,13 @@ class WasmFullDecoder : public WasmDecoder {
return bytes;
}
bool CheckHasMemory() {
if (!module_->has_memory) {
error(pc_ - 1, "memory instruction with no memory");
}
return module_->has_memory;
}
// Decodes the body of a function.
void DecodeFunctionBody() {
TRACE("wasm-decode %p...%p (module+%d, %d bytes) %s\n",
@ -1112,6 +1119,7 @@ class WasmFullDecoder : public WasmDecoder {
len = DecodeStoreMem(kWasmF64, MachineType::Float64());
break;
case kExprGrowMemory: {
if (!CheckHasMemory()) break;
MemoryIndexOperand operand(this, pc_);
DCHECK_NOT_NULL(module_);
if (module_->origin != kAsmJsOrigin) {
@ -1124,6 +1132,7 @@ class WasmFullDecoder : public WasmDecoder {
break;
}
case kExprMemorySize: {
if (!CheckHasMemory()) break;
MemoryIndexOperand operand(this, pc_);
Push(kWasmI32, BUILD(CurrentMemoryPages));
len = 1 + operand.length;
@ -1304,6 +1313,7 @@ class WasmFullDecoder : public WasmDecoder {
void PopControl() { control_.pop_back(); }
int DecodeLoadMem(ValueType type, MachineType mem_type) {
if (!CheckHasMemory()) return 0;
MemoryAccessOperand operand(this, pc_,
ElementSizeLog2Of(mem_type.representation()));
@ -1315,6 +1325,7 @@ class WasmFullDecoder : public WasmDecoder {
}
int DecodeStoreMem(ValueType type, MachineType mem_type) {
if (!CheckHasMemory()) return 0;
MemoryAccessOperand operand(this, pc_,
ElementSizeLog2Of(mem_type.representation()));
Value val = Pop(1, type);

View File

@ -308,7 +308,10 @@ void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) {
MaybeLocal<Value> instance =
InstantiateModuleImpl(i_isolate, i_module_obj, args, &thrower);
if (instance.IsEmpty()) return;
if (instance.IsEmpty()) {
DCHECK(thrower.error());
return;
}
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
return_value.Set(instance.ToLocalChecked());

View File

@ -1293,7 +1293,7 @@ class WasmInstanceBuilder {
Address mem_start = static_cast<Address>(memory_->backing_store());
uint32_t mem_size =
static_cast<uint32_t>(memory_->byte_length()->Number());
LoadDataSegments(mem_start, mem_size);
if (!LoadDataSegments(mem_start, mem_size)) return nothing;
uint32_t old_mem_size = compiled_module_->mem_size();
Address old_mem_start =
@ -1305,7 +1305,7 @@ class WasmInstanceBuilder {
old_mem_size, mem_size);
compiled_module_->set_memory(memory_);
} else {
LoadDataSegments(nullptr, 0);
if (!LoadDataSegments(nullptr, 0)) return nothing;
}
//--------------------------------------------------------------------------
@ -1545,7 +1545,7 @@ class WasmInstanceBuilder {
}
// Load data segments into the memory.
void LoadDataSegments(Address mem_addr, size_t mem_size) {
bool LoadDataSegments(Address mem_addr, size_t mem_size) {
Handle<SeqOneByteString> module_bytes(compiled_module_->module_bytes(),
isolate_);
for (const WasmDataSegment& segment : module_->data_segments) {
@ -1553,18 +1553,19 @@ class WasmInstanceBuilder {
// Segments of size == 0 are just nops.
if (source_size == 0) continue;
uint32_t dest_offset = EvalUint32InitExpr(segment.dest_addr);
if (dest_offset >= mem_size || source_size >= mem_size ||
dest_offset > (mem_size - source_size)) {
if (dest_offset + source_size > mem_size ||
dest_offset + source_size < dest_offset) {
thrower_->LinkError("data segment (start = %" PRIu32 ", size = %" PRIu32
") does not fit into memory (size = %" PRIuS ")",
dest_offset, source_size, mem_size);
return;
return false;
}
byte* dest = mem_addr + dest_offset;
const byte* src = reinterpret_cast<const byte*>(
module_bytes->GetCharsAddress() + segment.source_offset);
memcpy(dest, src, source_size);
}
return true;
}
void WriteGlobalValue(WasmGlobal& global, Handle<Object> value) {

View File

@ -1808,6 +1808,7 @@ TEST(Build_Wasm_UnreachableIf2) {
WASM_EXEC_TEST(Unreachable_Load) {
WasmRunner<int32_t, int32_t> r(execution_mode);
r.module().AddMemory(0L);
BUILD(r, WASM_BLOCK_I(WASM_BRV(0, WASM_GET_LOCAL(0)),
WASM_LOAD_MEM(MachineType::Int8(), WASM_GET_LOCAL(0))));
CHECK_EQ(11, r.Call(11));

View File

@ -101,6 +101,7 @@ TEST(IllegalLoad) {
TestSignatures sigs;
// Set the execution context, such that a runtime error can be thrown.
r.SetModuleContext();
r.module().AddMemory(0L);
BUILD(r, WASM_IF(WASM_ONE, WASM_SEQ(WASM_LOAD_MEM(MachineType::Int32(),
WASM_I32V_1(-3)),

View File

@ -105,8 +105,10 @@ class TestingModule : public ModuleEnv {
void ChangeOriginToAsmjs() { module_.origin = kAsmJsOrigin; }
byte* AddMemory(uint32_t size) {
CHECK(!module_.has_memory);
CHECK_NULL(instance->mem_start);
CHECK_EQ(0, instance->mem_size);
module_.has_memory = true;
instance->mem_start = reinterpret_cast<byte*>(malloc(size));
CHECK(instance->mem_start);
memset(instance->mem_start, 0, size);

View File

@ -9,6 +9,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
(function() {
var builder = new WasmModuleBuilder();
builder.addMemory(0, 0, false);
builder.addFunction("test", kSig_i_iii)
.addBody([
kExprI32Const, 0x0b,

View File

@ -184,6 +184,7 @@ testGrowMemoryReadWrite8();
function testGrowMemoryZeroInitialSize() {
var builder = genGrowMemoryBuilder();
builder.addMemory(0, kV8MaxPages, false);
var module = builder.instantiate();
var offset;
function peek() { return module.exports.load(offset); }
@ -217,6 +218,7 @@ testGrowMemoryZeroInitialSize();
function testGrowMemoryZeroInitialSize32() {
var builder = genGrowMemoryBuilder();
builder.addMemory(0, kV8MaxPages, false);
var module = builder.instantiate();
var offset;
function peek() { return module.exports.load(offset); }
@ -242,6 +244,7 @@ testGrowMemoryZeroInitialSize32();
function testGrowMemoryZeroInitialSize16() {
var builder = genGrowMemoryBuilder();
builder.addMemory(0, kV8MaxPages, false);
var module = builder.instantiate();
var offset;
function peek() { return module.exports.load16(offset); }
@ -267,6 +270,7 @@ testGrowMemoryZeroInitialSize16();
function testGrowMemoryZeroInitialSize8() {
var builder = genGrowMemoryBuilder();
builder.addMemory(0, kV8MaxPages, false);
var module = builder.instantiate();
var offset;
function peek() { return module.exports.load8(offset); }
@ -292,6 +296,7 @@ testGrowMemoryZeroInitialSize8();
function testGrowMemoryTrapMaxPagesZeroInitialMemory() {
var builder = genGrowMemoryBuilder();
builder.addMemory(0, kV8MaxPages, false);
var module = builder.instantiate();
var maxPages = 16385;
function growMem(pages) { return module.exports.grow_memory(pages); }
@ -313,6 +318,7 @@ testGrowMemoryTrapMaxPages();
function testGrowMemoryTrapsWithNonSmiInput() {
var builder = genGrowMemoryBuilder();
builder.addMemory(0, kV8MaxPages, false);
var module = builder.instantiate();
function growMem(pages) { return module.exports.grow_memory(pages); }
// The parameter of grow_memory is unsigned. Therefore -1 stands for

View File

@ -10,6 +10,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
(function testMemorySizeZero() {
print("testMemorySizeZero()");
var builder = new WasmModuleBuilder();
builder.addMemory(0, 0, false);
builder.addFunction("memory_size", kSig_i_v)
.addBody([kExprMemorySize, kMemoryZero])
.exportFunc();

View File

@ -42,6 +42,8 @@ function STACK() {
var builder = new WasmModuleBuilder();
builder.addMemory(0, 1, false);
builder.addImport("mod", "func", kSig_v_v);
builder.addFunction("main", kSig_v_v)
@ -70,8 +72,8 @@ var module = builder.instantiate({mod: {func: STACK}});
// The line numbers below will change as this test gains / loses lines..
" at STACK (stack.js:39:11)\n" + // --
" at main (<WASM>[1]+1)\n" + // --
" at testSimpleStack (stack.js:76:18)\n" + // --
" at stack.js:78:3"; // --
" at testSimpleStack (stack.js:78:18)\n" + // --
" at stack.js:80:3"; // --
module.exports.main();
assertEquals(expected_string, stripPath(stack));
@ -90,8 +92,8 @@ Error.prepareStackTrace = function(error, frames) {
// isWasm function line pos file
[ false, "STACK", 39, 0, "stack.js"],
[ true, "main", 1, 1, null],
[ false, "testStackFrames", 87, 0, "stack.js"],
[ false, null, 96, 0, "stack.js"]
[ false, "testStackFrames", 89, 0, "stack.js"],
[ false, null, 98, 0, "stack.js"]
]);
})();
@ -104,8 +106,8 @@ Error.prepareStackTrace = function(error, frames) {
verifyStack(e.stack, [
// isWasm function line pos file
[ true, "exec_unreachable", 2, 1, null],
[ false, "testWasmUnreachable", 100, 0, "stack.js"],
[ false, null, 111, 0, "stack.js"]
[ false, "testWasmUnreachable", 102, 0, "stack.js"],
[ false, null, 113, 0, "stack.js"]
]);
}
})();
@ -120,8 +122,8 @@ Error.prepareStackTrace = function(error, frames) {
// isWasm function line pos file
[ true, "", 3, 3, null],
[ true, "call_mem_out_of_bounds", 4, 1, null],
[ false, "testWasmMemOutOfBounds", 115, 0, "stack.js"],
[ false, null, 127, 0, "stack.js"]
[ false, "testWasmMemOutOfBounds", 117, 0, "stack.js"],
[ false, null, 129, 0, "stack.js"]
]);
}
})();

View File

@ -14,6 +14,8 @@ Error.prepareStackTrace = function(error, frames) {
var builder = new WasmModuleBuilder();
builder.addMemory(0, 1, false);
var sig_index = builder.addType(kSig_i_v)
// Build a function to resemble this code:

View File

@ -14,6 +14,8 @@ Error.prepareStackTrace = function(error, frames) {
var builder = new WasmModuleBuilder();
builder.addMemory(0, 1, false);
var sig_index = builder.addType(kSig_i_v)
// Build a function to resemble this code:

View File

@ -201,6 +201,60 @@ class FunctionBodyDecoderTest : public TestWithZone {
}
};
namespace {
// A helper for tests that require a module environment for functions,
// globals, or memories.
class TestModuleEnv : public ModuleEnv {
public:
explicit TestModuleEnv(ModuleOrigin origin = kWasmOrigin)
: ModuleEnv(&mod, nullptr) {
mod.origin = origin;
}
byte AddGlobal(ValueType type, bool mutability = true) {
mod.globals.push_back({type, mutability, WasmInitExpr(), 0, false, false});
CHECK(mod.globals.size() <= 127);
return static_cast<byte>(mod.globals.size() - 1);
}
byte AddSignature(FunctionSig* sig) {
mod.signatures.push_back(sig);
CHECK(mod.signatures.size() <= 127);
return static_cast<byte>(mod.signatures.size() - 1);
}
byte AddFunction(FunctionSig* sig) {
mod.functions.push_back({sig, // sig
0, // func_index
0, // sig_index
0, // name_offset
0, // name_length
0, // code_start_offset
0, // code_end_offset
false, // import
false}); // export
CHECK(mod.functions.size() <= 127);
return static_cast<byte>(mod.functions.size() - 1);
}
byte AddImport(FunctionSig* sig) {
byte result = AddFunction(sig);
mod.functions[result].imported = true;
return result;
}
void InitializeMemory() {
mod.has_memory = true;
mod.min_mem_pages = 1;
mod.max_mem_pages = 100;
}
void InitializeFunctionTable() {
mod.function_tables.push_back(
{0, 0, true, std::vector<int32_t>(), false, false, SignatureMap()});
}
private:
WasmModule mod;
};
} // namespace
TEST_F(FunctionBodyDecoderTest, Int32Const1) {
byte code[] = {kExprI32Const, 0};
for (int i = -64; i <= 63; i++) {
@ -1023,6 +1077,9 @@ TEST_F(FunctionBodyDecoderTest, TypeConversions) {
}
TEST_F(FunctionBodyDecoderTest, MacrosStmt) {
TestModuleEnv module_env;
module = &module_env;
module_env.InitializeMemory();
EXPECT_VERIFIES(v_i, WASM_SET_LOCAL(0, WASM_I32V_3(87348)));
EXPECT_VERIFIES(v_i, WASM_STORE_MEM(MachineType::Int32(), WASM_I32V_1(24),
WASM_I32V_1(40)));
@ -1159,12 +1216,18 @@ TEST_F(FunctionBodyDecoderTest, AllSimpleExpressions) {
}
TEST_F(FunctionBodyDecoderTest, MemorySize) {
TestModuleEnv module_env;
module = &module_env;
module_env.InitializeMemory();
byte code[] = {kExprMemorySize, 0};
EXPECT_VERIFIES_C(i_i, code);
EXPECT_FAILURE_C(f_ff, code);
}
TEST_F(FunctionBodyDecoderTest, LoadMemOffset) {
TestModuleEnv module_env;
module = &module_env;
module_env.InitializeMemory();
for (int offset = 0; offset < 128; offset += 7) {
byte code[] = {kExprI32Const, 0, kExprI32LoadMem, ZERO_ALIGNMENT,
static_cast<byte>(offset)};
@ -1173,6 +1236,9 @@ TEST_F(FunctionBodyDecoderTest, LoadMemOffset) {
}
TEST_F(FunctionBodyDecoderTest, LoadMemAlignment) {
TestModuleEnv module_env;
module = &module_env;
module_env.InitializeMemory();
struct {
WasmOpcode instruction;
uint32_t maximum_aligment;
@ -1207,6 +1273,9 @@ TEST_F(FunctionBodyDecoderTest, LoadMemAlignment) {
}
TEST_F(FunctionBodyDecoderTest, StoreMemOffset) {
TestModuleEnv module_env;
module = &module_env;
module_env.InitializeMemory();
for (int offset = 0; offset < 128; offset += 7) {
byte code[] = {WASM_STORE_MEM_OFFSET(MachineType::Int32(), offset,
WASM_ZERO, WASM_ZERO)};
@ -1215,6 +1284,9 @@ TEST_F(FunctionBodyDecoderTest, StoreMemOffset) {
}
TEST_F(FunctionBodyDecoderTest, StoreMemOffset_void) {
TestModuleEnv module_env;
module = &module_env;
module_env.InitializeMemory();
EXPECT_FAILURE(i_i, WASM_STORE_MEM_OFFSET(MachineType::Int32(), 0, WASM_ZERO,
WASM_ZERO));
}
@ -1230,6 +1302,9 @@ TEST_F(FunctionBodyDecoderTest, StoreMemOffset_void) {
#define VARINT4(x) BYTE0(x) | 0x80, BYTE1(x) | 0x80, BYTE2(x) | 0x80, BYTE3(x)
TEST_F(FunctionBodyDecoderTest, LoadMemOffset_varint) {
TestModuleEnv module_env;
module = &module_env;
module_env.InitializeMemory();
EXPECT_VERIFIES(i_i, WASM_ZERO, kExprI32LoadMem, ZERO_ALIGNMENT,
VARINT1(0x45));
EXPECT_VERIFIES(i_i, WASM_ZERO, kExprI32LoadMem, ZERO_ALIGNMENT,
@ -1241,6 +1316,9 @@ TEST_F(FunctionBodyDecoderTest, LoadMemOffset_varint) {
}
TEST_F(FunctionBodyDecoderTest, StoreMemOffset_varint) {
TestModuleEnv module_env;
module = &module_env;
module_env.InitializeMemory();
EXPECT_VERIFIES(v_i, WASM_ZERO, WASM_ZERO, kExprI32StoreMem, ZERO_ALIGNMENT,
VARINT1(0x33));
EXPECT_VERIFIES(v_i, WASM_ZERO, WASM_ZERO, kExprI32StoreMem, ZERO_ALIGNMENT,
@ -1252,6 +1330,9 @@ TEST_F(FunctionBodyDecoderTest, StoreMemOffset_varint) {
}
TEST_F(FunctionBodyDecoderTest, AllLoadMemCombinations) {
TestModuleEnv module_env;
module = &module_env;
module_env.InitializeMemory();
for (size_t i = 0; i < arraysize(kValueTypes); i++) {
ValueType local_type = kValueTypes[i];
for (size_t j = 0; j < arraysize(machineTypes); j++) {
@ -1268,6 +1349,9 @@ TEST_F(FunctionBodyDecoderTest, AllLoadMemCombinations) {
}
TEST_F(FunctionBodyDecoderTest, AllStoreMemCombinations) {
TestModuleEnv module_env;
module = &module_env;
module_env.InitializeMemory();
for (size_t i = 0; i < arraysize(kValueTypes); i++) {
ValueType local_type = kValueTypes[i];
for (size_t j = 0; j < arraysize(machineTypes); j++) {
@ -1283,54 +1367,6 @@ TEST_F(FunctionBodyDecoderTest, AllStoreMemCombinations) {
}
}
namespace {
// A helper for tests that require a module environment for functions and
// globals.
class TestModuleEnv : public ModuleEnv {
public:
explicit TestModuleEnv(ModuleOrigin origin = kWasmOrigin)
: ModuleEnv(&mod, nullptr) {
mod.origin = origin;
}
byte AddGlobal(ValueType type, bool mutability = true) {
mod.globals.push_back({type, mutability, WasmInitExpr(), 0, false, false});
CHECK(mod.globals.size() <= 127);
return static_cast<byte>(mod.globals.size() - 1);
}
byte AddSignature(FunctionSig* sig) {
mod.signatures.push_back(sig);
CHECK(mod.signatures.size() <= 127);
return static_cast<byte>(mod.signatures.size() - 1);
}
byte AddFunction(FunctionSig* sig) {
mod.functions.push_back({sig, // sig
0, // func_index
0, // sig_index
0, // name_offset
0, // name_length
0, // code_start_offset
0, // code_end_offset
false, // import
false}); // export
CHECK(mod.functions.size() <= 127);
return static_cast<byte>(mod.functions.size() - 1);
}
byte AddImport(FunctionSig* sig) {
byte result = AddFunction(sig);
mod.functions[result].imported = true;
return result;
}
void InitializeFunctionTable() {
mod.function_tables.push_back(
{0, 0, true, std::vector<int32_t>(), false, false, SignatureMap()});
}
private:
WasmModule mod;
};
} // namespace
TEST_F(FunctionBodyDecoderTest, SimpleCalls) {
FunctionSig* sig = sigs.i_i();
TestModuleEnv module_env;
@ -1665,6 +1701,7 @@ TEST_F(FunctionBodyDecoderTest, AllSetGlobalCombinations) {
TEST_F(FunctionBodyDecoderTest, WasmGrowMemory) {
TestModuleEnv module_env;
module = &module_env;
module_env.InitializeMemory();
byte code[] = {WASM_GET_LOCAL(0), kExprGrowMemory, 0};
EXPECT_VERIFIES_C(i_i, code);
@ -1674,6 +1711,7 @@ TEST_F(FunctionBodyDecoderTest, WasmGrowMemory) {
TEST_F(FunctionBodyDecoderTest, AsmJsGrowMemory) {
TestModuleEnv module_env(kAsmJsOrigin);
module = &module_env;
module_env.InitializeMemory();
byte code[] = {WASM_GET_LOCAL(0), kExprGrowMemory, 0};
EXPECT_FAILURE_C(i_i, code);
@ -1705,6 +1743,7 @@ TEST_F(FunctionBodyDecoderTest, AsmJsBinOpsCheckOrigin) {
{
TestModuleEnv module_env(kAsmJsOrigin);
module = &module_env;
module_env.InitializeMemory();
for (size_t i = 0; i < arraysize(AsmJsBinOps); i++) {
TestBinop(AsmJsBinOps[i].op, AsmJsBinOps[i].sig);
}
@ -1713,6 +1752,7 @@ TEST_F(FunctionBodyDecoderTest, AsmJsBinOpsCheckOrigin) {
{
TestModuleEnv module_env;
module = &module_env;
module_env.InitializeMemory();
for (size_t i = 0; i < arraysize(AsmJsBinOps); i++) {
byte code[] = {
WASM_BINOP(AsmJsBinOps[i].op, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))};
@ -1751,6 +1791,7 @@ TEST_F(FunctionBodyDecoderTest, AsmJsUnOpsCheckOrigin) {
{
TestModuleEnv module_env(kAsmJsOrigin);
module = &module_env;
module_env.InitializeMemory();
for (size_t i = 0; i < arraysize(AsmJsUnOps); i++) {
TestUnop(AsmJsUnOps[i].op, AsmJsUnOps[i].sig);
}
@ -1759,6 +1800,7 @@ TEST_F(FunctionBodyDecoderTest, AsmJsUnOpsCheckOrigin) {
{
TestModuleEnv module_env;
module = &module_env;
module_env.InitializeMemory();
for (size_t i = 0; i < arraysize(AsmJsUnOps); i++) {
byte code[] = {WASM_UNOP(AsmJsUnOps[i].op, WASM_GET_LOCAL(0))};
EXPECT_FAILURE_SC(AsmJsUnOps[i].sig, code);