Reland "[asmjs] Properly validate asm.js heap sizes"
This is a reland of5c3092718e
(the CL was reverted because of a Chromium test that is now fixed) Original change's description: > Reland "[asmjs] Properly validate asm.js heap sizes" > > This is a reland of5d69010e26
> > Original change's description: > > [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: Michael Starzinger <mstarzinger@chromium.org> > > Cr-Commit-Position: refs/heads/master@{#55163} > > Bug: chromium:873600 > Change-Id: Id24070bda3aafb9e1a32af0732a1b18f633ef932 > Reviewed-on: https://chromium-review.googlesource.com/1179681 > Commit-Queue: Ben Titzer <titzer@chromium.org> > Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> > Cr-Commit-Position: refs/heads/master@{#55193} Bug: chromium:873600 Change-Id: I6eca2a89589070837b109278f964fc8e9a0fd6f1 Reviewed-on: https://chromium-review.googlesource.com/1183081 Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> Reviewed-by: Maya Lekova <mslekova@chromium.org> Commit-Queue: Ben Titzer <titzer@chromium.org> Cr-Commit-Position: refs/heads/master@{#55249}
This commit is contained in:
parent
c4d1a00873
commit
438e7ec6dc
@ -23,6 +23,7 @@
|
||||
|
||||
#include "src/wasm/wasm-engine.h"
|
||||
#include "src/wasm/wasm-js.h"
|
||||
#include "src/wasm/wasm-limits.h"
|
||||
#include "src/wasm/wasm-module-builder.h"
|
||||
#include "src/wasm/wasm-objects-inl.h"
|
||||
#include "src/wasm/wasm-result.h"
|
||||
@ -329,6 +330,28 @@ UnoptimizedCompilationJob* AsmJs::NewCompilationJob(
|
||||
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,
|
||||
Handle<SharedFunctionInfo> shared,
|
||||
Handle<FixedArray> wasm_data,
|
||||
@ -369,15 +392,9 @@ MaybeHandle<Object> AsmJs::InstantiateAsmWasm(Isolate* isolate,
|
||||
}
|
||||
memory->set_is_growable(false);
|
||||
size_t size = NumberToSize(memory->byte_length());
|
||||
// TODO(mstarzinger): We currently only limit byte length of the buffer to
|
||||
// be a multiple of 8, we should enforce the stricter spec limits here.
|
||||
if (size % FixedTypedArrayBase::kMaxElementSize != 0) {
|
||||
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");
|
||||
// Check the asm.js heap size against the valid limits.
|
||||
if (!IsValidAsmjsMemorySize(size)) {
|
||||
ReportInstantiationFailure(script, position, "Invalid heap size");
|
||||
return MaybeHandle<Object>();
|
||||
}
|
||||
} else {
|
||||
|
@ -2793,9 +2793,6 @@ bool Shell::SetOptions(int argc, char* argv[]) {
|
||||
strcmp(argv[i], "--no-stress-background-compile") == 0) {
|
||||
options.stress_background_compile = false;
|
||||
argv[i] = nullptr;
|
||||
} else if (strcmp(argv[i], "--mock-arraybuffer-allocator") == 0) {
|
||||
options.mock_arraybuffer_allocator = true;
|
||||
argv[i] = nullptr;
|
||||
} else if (strcmp(argv[i], "--noalways-opt") == 0 ||
|
||||
strcmp(argv[i], "--no-always-opt") == 0) {
|
||||
// No support for stressing if we can't use --always-opt.
|
||||
@ -2910,6 +2907,7 @@ bool Shell::SetOptions(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
|
||||
options.mock_arraybuffer_allocator = i::FLAG_mock_arraybuffer_allocator;
|
||||
|
||||
// Set up isolated source groups.
|
||||
options.isolate_sources = new SourceGroup[options.num_isolates];
|
||||
|
@ -978,7 +978,6 @@ DEFINE_INT(heap_snapshot_string_limit, 1024,
|
||||
DEFINE_BOOL(sampling_heap_profiler_suppress_randomness, false,
|
||||
"Use constant sample intervals to eliminate test flakiness")
|
||||
|
||||
|
||||
// v8.cc
|
||||
DEFINE_BOOL(use_idle_notification, true,
|
||||
"Use idle notification to reduce memory footprint.")
|
||||
@ -1145,6 +1144,8 @@ DEFINE_BOOL(use_external_strings, false, "Use external strings for source code")
|
||||
DEFINE_STRING(map_counters, "", "Map counters to a file")
|
||||
DEFINE_ARGS(js_arguments,
|
||||
"Pass all remaining arguments to the script. Alias for \"--\".")
|
||||
DEFINE_BOOL(mock_arraybuffer_allocator, false,
|
||||
"Use a mock ArrayBuffer allocator for testing.")
|
||||
|
||||
//
|
||||
// GDB JIT integration flags.
|
||||
|
@ -951,14 +951,17 @@ void SetInstanceMemory(Handle<WasmInstanceObject> instance,
|
||||
instance->SetRawMemory(reinterpret_cast<byte*>(buffer->backing_store()),
|
||||
buffer->byte_length()->Number());
|
||||
#if DEBUG
|
||||
// To flush out bugs earlier, in DEBUG mode, check that all pages of the
|
||||
// memory are accessible by reading and writing one byte on each page.
|
||||
byte* mem_start = instance->memory_start();
|
||||
size_t mem_size = instance->memory_size();
|
||||
for (size_t offset = 0; offset < mem_size; offset += wasm::kWasmPageSize) {
|
||||
byte val = mem_start[offset];
|
||||
USE(val);
|
||||
mem_start[offset] = val;
|
||||
if (!FLAG_mock_arraybuffer_allocator) {
|
||||
// To flush out bugs earlier, in DEBUG mode, check that all pages of the
|
||||
// memory are accessible by reading and writing one byte on each page.
|
||||
// Don't do this if the mock ArrayBuffer allocator is enabled.
|
||||
byte* mem_start = instance->memory_start();
|
||||
size_t mem_size = instance->memory_size();
|
||||
for (size_t offset = 0; offset < mem_size; offset += wasm::kWasmPageSize) {
|
||||
byte val = mem_start[offset];
|
||||
USE(val);
|
||||
mem_start[offset] = val;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -26424,7 +26424,7 @@ TEST(TurboAsmDisablesNeuter) {
|
||||
" function load() { return MEM32[0] | 0; }"
|
||||
" return { load: load };"
|
||||
"}"
|
||||
"var buffer = new ArrayBuffer(1024);"
|
||||
"var buffer = new ArrayBuffer(4096);"
|
||||
"var module = Module(this, {}, buffer);"
|
||||
"%OptimizeFunctionOnNextCall(module.load);"
|
||||
"module.load();"
|
||||
@ -26440,7 +26440,7 @@ TEST(TurboAsmDisablesNeuter) {
|
||||
" function store() { MEM32[0] = 0; }"
|
||||
" return { store: store };"
|
||||
"}"
|
||||
"var buffer = new ArrayBuffer(1024);"
|
||||
"var buffer = new ArrayBuffer(4096);"
|
||||
"var module = Module(this, {}, buffer);"
|
||||
"%OptimizeFunctionOnNextCall(module.store);"
|
||||
"module.store();"
|
||||
|
@ -2,4 +2,4 @@
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# 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
|
||||
|
99
test/mjsunit/asm/asm-heap.js
Normal file
99
test/mjsunit/asm/asm-heap.js
Normal file
@ -0,0 +1,99 @@
|
||||
// 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 --mock-arraybuffer-allocator
|
||||
|
||||
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 @@
|
||||
'asm/poppler/*': [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.
|
||||
'copy-on-write-assert': [PASS, SLOW],
|
||||
'es6/typedarray-construct-offset-not-smi': [PASS, SLOW],
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
let kMinHeapSize = 4096;
|
||||
|
||||
(function TestLeftRight() {
|
||||
function Module(stdlib, foreign, heap) {
|
||||
"use asm";
|
||||
@ -14,7 +16,7 @@
|
||||
}
|
||||
return { f:f }
|
||||
}
|
||||
var buffer = new ArrayBuffer(1024);
|
||||
var buffer = new ArrayBuffer(kMinHeapSize);
|
||||
var module = new Module(this, {}, buffer);
|
||||
assertTrue(%IsAsmWasmCode(Module));
|
||||
new Int32Array(buffer)[42] = 23;
|
||||
@ -31,7 +33,7 @@
|
||||
}
|
||||
return { f:f }
|
||||
}
|
||||
var buffer = new ArrayBuffer(1024);
|
||||
var buffer = new ArrayBuffer(kMinHeapSize);
|
||||
var module = new Module(this, {}, buffer)
|
||||
assertTrue(%IsAsmWasmCode(Module));
|
||||
new Int32Array(buffer)[42 >> 4] = 23;
|
||||
@ -48,7 +50,7 @@
|
||||
}
|
||||
return { f:f }
|
||||
}
|
||||
var buffer = new ArrayBuffer(1024);
|
||||
var buffer = new ArrayBuffer(kMinHeapSize);
|
||||
var module = new Module(this, {}, buffer)
|
||||
assertFalse(%IsAsmWasmCode(Module));
|
||||
new Int32Array(buffer)[42 & 0xfc] = 23;
|
||||
@ -65,7 +67,7 @@
|
||||
}
|
||||
return { f:f }
|
||||
}
|
||||
var buffer = new ArrayBuffer(1024);
|
||||
var buffer = new ArrayBuffer(kMinHeapSize);
|
||||
var module = new Module(this, {}, buffer)
|
||||
assertFalse(%IsAsmWasmCode(Module));
|
||||
new Int32Array(buffer)[42 >> 3] = 23;
|
||||
@ -82,7 +84,7 @@
|
||||
}
|
||||
return { f:f }
|
||||
}
|
||||
var buffer = new ArrayBuffer(1024);
|
||||
var buffer = new ArrayBuffer(kMinHeapSize);
|
||||
var module = new Module(this, {}, buffer)
|
||||
assertFalse(%IsAsmWasmCode(Module));
|
||||
new Int32Array(buffer)[42 << 2] = 23;
|
||||
|
@ -13,7 +13,7 @@ function Module(stdlib, env, heap) {
|
||||
return { f: f };
|
||||
}
|
||||
function instantiate() {
|
||||
var buffer = new ArrayBuffer(0);
|
||||
var buffer = new ArrayBuffer(4096);
|
||||
Module(this, {}, buffer).f();
|
||||
try {} finally {}
|
||||
gc();
|
||||
|
@ -16,7 +16,7 @@ function module(stdlib,foreign,buffer) {
|
||||
|
||||
var global = {Uint32Array:Uint32Array};
|
||||
var env = {};
|
||||
memory = new WebAssembly.Memory({initial:200});
|
||||
memory = new WebAssembly.Memory({initial:128});
|
||||
var buffer = memory.buffer;
|
||||
evil_f = module(global,env,buffer);
|
||||
|
||||
|
50
test/mjsunit/regress/wasm/regress-873600.js
Normal file
50
test/mjsunit/regress/wasm/regress-873600.js
Normal file
@ -0,0 +1,50 @@
|
||||
// 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 @@
|
||||
// Flags: --validate-asm --allow-natives-syntax
|
||||
|
||||
var stdlib = this;
|
||||
let kMinHeapSize = 4096;
|
||||
|
||||
function assertValidAsm(func) {
|
||||
assertTrue(%IsAsmWasmCode(func), "must be valid asm code");
|
||||
@ -13,7 +14,7 @@ function assertValidAsm(func) {
|
||||
function assertWasm(expected, func, ffi) {
|
||||
print("Testing " + func.name + "...");
|
||||
assertEquals(
|
||||
expected, func(stdlib, ffi, new ArrayBuffer(1024)).caller());
|
||||
expected, func(stdlib, ffi, new ArrayBuffer(kMinHeapSize)).caller());
|
||||
assertValidAsm(func);
|
||||
}
|
||||
|
||||
@ -38,7 +39,7 @@ assertWasm(7, TestInt32HeapAccess);
|
||||
|
||||
|
||||
function TestInt32HeapAccessExternal() {
|
||||
var memory = new ArrayBuffer(1024);
|
||||
var memory = new ArrayBuffer(kMinHeapSize);
|
||||
var memory_int32 = new Int32Array(memory);
|
||||
var module_decl = eval('(' + TestInt32HeapAccess.toString() + ')');
|
||||
var module = module_decl(stdlib, null, memory);
|
||||
@ -63,7 +64,7 @@ function TestHeapAccessIntTypes() {
|
||||
var code = TestInt32HeapAccess.toString();
|
||||
code = code.replace('Int32Array', types[i][1]);
|
||||
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 module_decl = eval('(' + code + ')');
|
||||
var module = module_decl(stdlib, null, memory);
|
||||
@ -102,7 +103,7 @@ assertWasm(1, TestFloatHeapAccess);
|
||||
|
||||
|
||||
function TestFloatHeapAccessExternal() {
|
||||
var memory = new ArrayBuffer(1024);
|
||||
var memory = new ArrayBuffer(kMinHeapSize);
|
||||
var memory_float64 = new Float64Array(memory);
|
||||
var module_decl = eval('(' + TestFloatHeapAccess.toString() + ')');
|
||||
var module = module_decl(stdlib, null, memory);
|
||||
@ -146,7 +147,7 @@ TestFloatHeapAccessExternal();
|
||||
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 m = module_decl(stdlib, null, memory);
|
||||
assertValidAsm(module_decl);
|
||||
|
@ -5,6 +5,7 @@
|
||||
// Flags: --validate-asm --allow-natives-syntax
|
||||
|
||||
var stdlib = this;
|
||||
let kMinHeapSize = 4096;
|
||||
|
||||
function assertValidAsm(func) {
|
||||
assertTrue(%IsAsmWasmCode(func), "must be valid asm code");
|
||||
@ -13,7 +14,7 @@ function assertValidAsm(func) {
|
||||
function assertWasm(expected, func, ffi) {
|
||||
print("Testing " + func.name + "...");
|
||||
assertEquals(
|
||||
expected, func(stdlib, ffi, new ArrayBuffer(1024)).caller());
|
||||
expected, func(stdlib, ffi, new ArrayBuffer(kMinHeapSize)).caller());
|
||||
assertValidAsm(func);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user