Commit 5d69010e authored by Ben L. Titzer's avatar Ben L. Titzer Committed by Commit Bot

[asmjs] Properly validate asm.js heap sizes

Enforce both engine limitations and spec (http://asmjs.org/spec/latest/)
limitations on the size of asm.js heaps.

R=clemensh@chromium.org
CC=​mstarzinger@chromium.org

Bug: chromium:873600
Change-Id: I104c23bbd0a9a7c494f97f8f9e83ac5a37496dfd
Reviewed-on: https://chromium-review.googlesource.com/1174411
Commit-Queue: Ben Titzer <titzer@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55163}
parent 53a3eef1
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "src/wasm/wasm-engine.h" #include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-js.h" #include "src/wasm/wasm-js.h"
#include "src/wasm/wasm-limits.h"
#include "src/wasm/wasm-module-builder.h" #include "src/wasm/wasm-module-builder.h"
#include "src/wasm/wasm-objects-inl.h" #include "src/wasm/wasm-objects-inl.h"
#include "src/wasm/wasm-result.h" #include "src/wasm/wasm-result.h"
...@@ -329,6 +330,28 @@ UnoptimizedCompilationJob* AsmJs::NewCompilationJob( ...@@ -329,6 +330,28 @@ UnoptimizedCompilationJob* AsmJs::NewCompilationJob(
return new AsmJsCompilationJob(parse_info, literal, allocator); return new AsmJsCompilationJob(parse_info, literal, allocator);
} }
namespace {
inline bool IsValidAsmjsMemorySize(size_t size) {
// Enforce asm.js spec minimum size.
if (size < (1u << 12u)) return false;
// Enforce engine-limited maximum allocation size.
if (size > wasm::kV8MaxWasmMemoryBytes) return false;
// Enforce flag-limited maximum allocation size.
if (size > (FLAG_wasm_max_mem_pages * uint64_t{wasm::kWasmPageSize})) {
return false;
}
// Enforce power-of-2 sizes for 2^12 - 2^24.
if (size < (1u << 24u)) {
uint32_t size32 = static_cast<uint32_t>(size);
return base::bits::IsPowerOfTwo(size32);
}
// Enforce multiple of 2^24 for sizes >= 2^24
if ((size % (1u << 24u)) != 0) return false;
// All checks passed!
return true;
}
} // namespace
MaybeHandle<Object> AsmJs::InstantiateAsmWasm(Isolate* isolate, MaybeHandle<Object> AsmJs::InstantiateAsmWasm(Isolate* isolate,
Handle<SharedFunctionInfo> shared, Handle<SharedFunctionInfo> shared,
Handle<FixedArray> wasm_data, Handle<FixedArray> wasm_data,
...@@ -369,15 +392,9 @@ MaybeHandle<Object> AsmJs::InstantiateAsmWasm(Isolate* isolate, ...@@ -369,15 +392,9 @@ MaybeHandle<Object> AsmJs::InstantiateAsmWasm(Isolate* isolate,
} }
memory->set_is_growable(false); memory->set_is_growable(false);
size_t size = NumberToSize(memory->byte_length()); size_t size = NumberToSize(memory->byte_length());
// TODO(mstarzinger): We currently only limit byte length of the buffer to // Check the asm.js heap size against the valid limits.
// be a multiple of 8, we should enforce the stricter spec limits here. if (!IsValidAsmjsMemorySize(size)) {
if (size % FixedTypedArrayBase::kMaxElementSize != 0) { ReportInstantiationFailure(script, position, "Invalid heap size");
ReportInstantiationFailure(script, position, "Unexpected heap size");
return MaybeHandle<Object>();
}
// Currently WebAssembly only supports heap sizes within the uint32_t range.
if (size > std::numeric_limits<uint32_t>::max()) {
ReportInstantiationFailure(script, position, "Unexpected heap size");
return MaybeHandle<Object>(); return MaybeHandle<Object>();
} }
} else { } else {
......
...@@ -26423,7 +26423,7 @@ TEST(TurboAsmDisablesNeuter) { ...@@ -26423,7 +26423,7 @@ TEST(TurboAsmDisablesNeuter) {
" function load() { return MEM32[0] | 0; }" " function load() { return MEM32[0] | 0; }"
" return { load: load };" " return { load: load };"
"}" "}"
"var buffer = new ArrayBuffer(1024);" "var buffer = new ArrayBuffer(4096);"
"var module = Module(this, {}, buffer);" "var module = Module(this, {}, buffer);"
"%OptimizeFunctionOnNextCall(module.load);" "%OptimizeFunctionOnNextCall(module.load);"
"module.load();" "module.load();"
...@@ -26439,7 +26439,7 @@ TEST(TurboAsmDisablesNeuter) { ...@@ -26439,7 +26439,7 @@ TEST(TurboAsmDisablesNeuter) {
" function store() { MEM32[0] = 0; }" " function store() { MEM32[0] = 0; }"
" return { store: store };" " return { store: store };"
"}" "}"
"var buffer = new ArrayBuffer(1024);" "var buffer = new ArrayBuffer(4096);"
"var module = Module(this, {}, buffer);" "var module = Module(this, {}, buffer);"
"%OptimizeFunctionOnNextCall(module.store);" "%OptimizeFunctionOnNextCall(module.store);"
"module.store();" "module.store();"
......
...@@ -2,4 +2,4 @@ ...@@ -2,4 +2,4 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
*%(basename)s:7: Linking failure in asm.js: Unexpected heap size *%(basename)s:7: Linking failure in asm.js: Invalid heap size
// Copyright 2018 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: --validate-asm --allow-natives-syntax --expose-gc
let gCounter = 1000;
let gMinHeap = new ArrayBuffer(1 << 12);
let gStdlib = {Uint8Array: Uint8Array};
// The template of asm.js modules used in this test.
function Template(stdlib, ffi, heap) {
"use asm";
var MEM8 = new stdlib.Uint8Array(heap);
function foo() { return VAL; }
return { foo: foo };
}
// Create a fresh module each time.
function NewModule() {
// Use eval() to get a unique module each time.
let val = gCounter++;
let string = (Template + "; Template").replace("VAL", "" + val);
// print(string);
let module = eval(string);
// print(module);
module(gStdlib, {}, gMinHeap);
assertTrue(%IsAsmWasmCode(module));
return {module: module, val: val};
}
(function TestValid_PowerOfTwo() {
print("TestValid_PowerOfTwo...");
let r = NewModule();
for (let i = 12; i <= 24; i++) {
gc(); // Likely OOM otherwise.
let size = 1 << i;
print(" size=" + size);
let heap = new ArrayBuffer(size);
var instance = r.module(gStdlib, {}, heap);
assertTrue(%IsAsmWasmCode(r.module));
assertEquals(r.val, instance.foo());
}
})();
(function TestValid_Multiple() {
print("TestValid_Multiple...");
let r = NewModule();
for (let i = 1; i < 47; i += 7) {
gc(); // Likely OOM otherwise.
let size = i * (1 << 24);
print(" size=" + size);
let heap = new ArrayBuffer(size);
var instance = r.module(gStdlib, {}, heap);
assertTrue(%IsAsmWasmCode(r.module));
assertEquals(r.val, instance.foo());
}
})();
(function TestInvalid_TooSmall() {
print("TestInvalid_TooSmall...");
for (let i = 1; i < 12; i++) {
let size = 1 << i;
print(" size=" + size);
let r = NewModule();
let heap = new ArrayBuffer(size);
var instance = r.module(gStdlib, {}, heap);
assertFalse(%IsAsmWasmCode(r.module));
assertEquals(r.val, instance.foo());
}
})();
(function TestInValid_NonPowerOfTwo() {
print("TestInvalid_NonPowerOfTwo...");
for (let i = 12; i <= 24; i++) {
gc(); // Likely OOM otherwise.
let size = 1 + (1 << i);
print(" size=" + size);
let r = NewModule();
let heap = new ArrayBuffer(size);
var instance = r.module(gStdlib, {}, heap);
assertFalse(%IsAsmWasmCode(r.module));
assertEquals(r.val, instance.foo());
}
})();
(function TestInValid_NonMultiple() {
print("TestInvalid_NonMultiple...");
for (let i = (1 << 24); i < (1 << 25); i += (1 << 22)) {
gc(); // Likely OOM otherwise.
let size = i + (1 << 20);
print(" size=" + size);
let r = NewModule();
let heap = new ArrayBuffer(size);
var instance = r.module(gStdlib, {}, heap);
assertFalse(%IsAsmWasmCode(r.module));
assertEquals(r.val, instance.foo());
}
})();
...@@ -154,6 +154,9 @@ ...@@ -154,6 +154,9 @@
'asm/poppler/*': [PASS, SLOW, NO_VARIANTS], 'asm/poppler/*': [PASS, SLOW, NO_VARIANTS],
'asm/sqlite3/*': [PASS, SLOW, NO_VARIANTS], 'asm/sqlite3/*': [PASS, SLOW, NO_VARIANTS],
# OOM flakes in isolates tests because too many largish heaps are created.
'asm/asm-heap': [PASS, NO_VARIANTS, ['isolates', SKIP]],
# Slow tests. # Slow tests.
'copy-on-write-assert': [PASS, SLOW], 'copy-on-write-assert': [PASS, SLOW],
'es6/typedarray-construct-offset-not-smi': [PASS, SLOW], 'es6/typedarray-construct-offset-not-smi': [PASS, SLOW],
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
// Flags: --allow-natives-syntax // Flags: --allow-natives-syntax
let kMinHeapSize = 4096;
(function TestLeftRight() { (function TestLeftRight() {
function Module(stdlib, foreign, heap) { function Module(stdlib, foreign, heap) {
"use asm"; "use asm";
...@@ -14,7 +16,7 @@ ...@@ -14,7 +16,7 @@
} }
return { f:f } return { f:f }
} }
var buffer = new ArrayBuffer(1024); var buffer = new ArrayBuffer(kMinHeapSize);
var module = new Module(this, {}, buffer); var module = new Module(this, {}, buffer);
assertTrue(%IsAsmWasmCode(Module)); assertTrue(%IsAsmWasmCode(Module));
new Int32Array(buffer)[42] = 23; new Int32Array(buffer)[42] = 23;
...@@ -31,7 +33,7 @@ ...@@ -31,7 +33,7 @@
} }
return { f:f } return { f:f }
} }
var buffer = new ArrayBuffer(1024); var buffer = new ArrayBuffer(kMinHeapSize);
var module = new Module(this, {}, buffer) var module = new Module(this, {}, buffer)
assertTrue(%IsAsmWasmCode(Module)); assertTrue(%IsAsmWasmCode(Module));
new Int32Array(buffer)[42 >> 4] = 23; new Int32Array(buffer)[42 >> 4] = 23;
...@@ -48,7 +50,7 @@ ...@@ -48,7 +50,7 @@
} }
return { f:f } return { f:f }
} }
var buffer = new ArrayBuffer(1024); var buffer = new ArrayBuffer(kMinHeapSize);
var module = new Module(this, {}, buffer) var module = new Module(this, {}, buffer)
assertFalse(%IsAsmWasmCode(Module)); assertFalse(%IsAsmWasmCode(Module));
new Int32Array(buffer)[42 & 0xfc] = 23; new Int32Array(buffer)[42 & 0xfc] = 23;
...@@ -65,7 +67,7 @@ ...@@ -65,7 +67,7 @@
} }
return { f:f } return { f:f }
} }
var buffer = new ArrayBuffer(1024); var buffer = new ArrayBuffer(kMinHeapSize);
var module = new Module(this, {}, buffer) var module = new Module(this, {}, buffer)
assertFalse(%IsAsmWasmCode(Module)); assertFalse(%IsAsmWasmCode(Module));
new Int32Array(buffer)[42 >> 3] = 23; new Int32Array(buffer)[42 >> 3] = 23;
...@@ -82,7 +84,7 @@ ...@@ -82,7 +84,7 @@
} }
return { f:f } return { f:f }
} }
var buffer = new ArrayBuffer(1024); var buffer = new ArrayBuffer(kMinHeapSize);
var module = new Module(this, {}, buffer) var module = new Module(this, {}, buffer)
assertFalse(%IsAsmWasmCode(Module)); assertFalse(%IsAsmWasmCode(Module));
new Int32Array(buffer)[42 << 2] = 23; new Int32Array(buffer)[42 << 2] = 23;
......
...@@ -13,7 +13,7 @@ function Module(stdlib, env, heap) { ...@@ -13,7 +13,7 @@ function Module(stdlib, env, heap) {
return { f: f }; return { f: f };
} }
function instantiate() { function instantiate() {
var buffer = new ArrayBuffer(0); var buffer = new ArrayBuffer(4096);
Module(this, {}, buffer).f(); Module(this, {}, buffer).f();
try {} finally {} try {} finally {}
gc(); gc();
......
...@@ -16,7 +16,7 @@ function module(stdlib,foreign,buffer) { ...@@ -16,7 +16,7 @@ function module(stdlib,foreign,buffer) {
var global = {Uint32Array:Uint32Array}; var global = {Uint32Array:Uint32Array};
var env = {}; var env = {};
memory = new WebAssembly.Memory({initial:200}); memory = new WebAssembly.Memory({initial:128});
var buffer = memory.buffer; var buffer = memory.buffer;
evil_f = module(global,env,buffer); evil_f = module(global,env,buffer);
......
// Copyright 2018 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.
(function DoTest() {
var stdlib = this;
try {
var buffer = new ArrayBuffer((2097120) * 1024);
} catch (e) {
// Out of memory: soft pass because 2GiB is actually a lot!
print("OOM: soft pass");
return;
}
var foreign = {}
var m = (function Module(stdlib, foreign, heap) {
"use asm";
var MEM16 = new stdlib.Int16Array(heap);
function load(i) {
i = i|0;
i = MEM16[i >> 1]|0;
return i | 0;
}
function store(i, v) {
i = i|0;
v = v|0;
MEM16[i >> 1] = v;
}
function load8(i) {
i = i|0;
i = MEM16[i + 8 >> 1]|0;
return i | 0;
}
function store8(i, v) {
i = i|0;
v = v|0;
MEM16[i + 8 >> 1] = v;
}
return { load: load, store: store, load8: load8, store8: store8 };
})(stdlib, foreign, buffer);
assertEquals(0, m.load(-8));
assertEquals(0, m.load8(-16));
m.store(2014, 2, 30, 1, 0);
assertEquals(0, m.load8(-8));
m.store8(-8, 99);
assertEquals(99, m.load(0));
assertEquals(99, m.load8(-8));
})();
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
// Flags: --validate-asm --allow-natives-syntax // Flags: --validate-asm --allow-natives-syntax
var stdlib = this; var stdlib = this;
let kMinHeapSize = 4096;
function assertValidAsm(func) { function assertValidAsm(func) {
assertTrue(%IsAsmWasmCode(func), "must be valid asm code"); assertTrue(%IsAsmWasmCode(func), "must be valid asm code");
...@@ -13,7 +14,7 @@ function assertValidAsm(func) { ...@@ -13,7 +14,7 @@ function assertValidAsm(func) {
function assertWasm(expected, func, ffi) { function assertWasm(expected, func, ffi) {
print("Testing " + func.name + "..."); print("Testing " + func.name + "...");
assertEquals( assertEquals(
expected, func(stdlib, ffi, new ArrayBuffer(1024)).caller()); expected, func(stdlib, ffi, new ArrayBuffer(kMinHeapSize)).caller());
assertValidAsm(func); assertValidAsm(func);
} }
...@@ -38,7 +39,7 @@ assertWasm(7, TestInt32HeapAccess); ...@@ -38,7 +39,7 @@ assertWasm(7, TestInt32HeapAccess);
function TestInt32HeapAccessExternal() { function TestInt32HeapAccessExternal() {
var memory = new ArrayBuffer(1024); var memory = new ArrayBuffer(kMinHeapSize);
var memory_int32 = new Int32Array(memory); var memory_int32 = new Int32Array(memory);
var module_decl = eval('(' + TestInt32HeapAccess.toString() + ')'); var module_decl = eval('(' + TestInt32HeapAccess.toString() + ')');
var module = module_decl(stdlib, null, memory); var module = module_decl(stdlib, null, memory);
...@@ -63,7 +64,7 @@ function TestHeapAccessIntTypes() { ...@@ -63,7 +64,7 @@ function TestHeapAccessIntTypes() {
var code = TestInt32HeapAccess.toString(); var code = TestInt32HeapAccess.toString();
code = code.replace('Int32Array', types[i][1]); code = code.replace('Int32Array', types[i][1]);
code = code.replace(/>> 2/g, types[i][2]); code = code.replace(/>> 2/g, types[i][2]);
var memory = new ArrayBuffer(1024); var memory = new ArrayBuffer(kMinHeapSize);
var memory_view = new types[i][0](memory); var memory_view = new types[i][0](memory);
var module_decl = eval('(' + code + ')'); var module_decl = eval('(' + code + ')');
var module = module_decl(stdlib, null, memory); var module = module_decl(stdlib, null, memory);
...@@ -102,7 +103,7 @@ assertWasm(1, TestFloatHeapAccess); ...@@ -102,7 +103,7 @@ assertWasm(1, TestFloatHeapAccess);
function TestFloatHeapAccessExternal() { function TestFloatHeapAccessExternal() {
var memory = new ArrayBuffer(1024); var memory = new ArrayBuffer(kMinHeapSize);
var memory_float64 = new Float64Array(memory); var memory_float64 = new Float64Array(memory);
var module_decl = eval('(' + TestFloatHeapAccess.toString() + ')'); var module_decl = eval('(' + TestFloatHeapAccess.toString() + ')');
var module = module_decl(stdlib, null, memory); var module = module_decl(stdlib, null, memory);
...@@ -146,7 +147,7 @@ TestFloatHeapAccessExternal(); ...@@ -146,7 +147,7 @@ TestFloatHeapAccessExternal();
return {load: load, iload: iload, store: store, storeb: storeb}; return {load: load, iload: iload, store: store, storeb: storeb};
} }
var memory = new ArrayBuffer(1024); var memory = new ArrayBuffer(kMinHeapSize);
var module_decl = eval('(' + TestByteHeapAccessCompat.toString() + ')'); var module_decl = eval('(' + TestByteHeapAccessCompat.toString() + ')');
var m = module_decl(stdlib, null, memory); var m = module_decl(stdlib, null, memory);
assertValidAsm(module_decl); assertValidAsm(module_decl);
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
// Flags: --validate-asm --allow-natives-syntax // Flags: --validate-asm --allow-natives-syntax
var stdlib = this; var stdlib = this;
let kMinHeapSize = 4096;
function assertValidAsm(func) { function assertValidAsm(func) {
assertTrue(%IsAsmWasmCode(func), "must be valid asm code"); assertTrue(%IsAsmWasmCode(func), "must be valid asm code");
...@@ -13,7 +14,7 @@ function assertValidAsm(func) { ...@@ -13,7 +14,7 @@ function assertValidAsm(func) {
function assertWasm(expected, func, ffi) { function assertWasm(expected, func, ffi) {
print("Testing " + func.name + "..."); print("Testing " + func.name + "...");
assertEquals( assertEquals(
expected, func(stdlib, ffi, new ArrayBuffer(1024)).caller()); expected, func(stdlib, ffi, new ArrayBuffer(kMinHeapSize)).caller());
assertValidAsm(func); assertValidAsm(func);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment