Commit fac176d8 authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[wasm] Fix memory limit checks

For memory limit checks, we should use the minimum of the
--wasm-max-mem-pages flag and kV8MaxWasmMemoryPages. The former is a
limit set by the user, the latter is the maximum we can handle
internally.

R=titzer@chromium.org

Bug: chromium:898677
Change-Id: I3c549f4e90dd016b5d07475d9353f30134f76dcc
Reviewed-on: https://chromium-review.googlesource.com/c/1305274
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarBen Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57127}
parent 1ff80455
...@@ -335,10 +335,8 @@ namespace { ...@@ -335,10 +335,8 @@ namespace {
inline bool IsValidAsmjsMemorySize(size_t size) { inline bool IsValidAsmjsMemorySize(size_t size) {
// Enforce asm.js spec minimum size. // Enforce asm.js spec minimum size.
if (size < (1u << 12u)) return false; if (size < (1u << 12u)) return false;
// Enforce engine-limited maximum allocation size. // Enforce engine-limited and flag-limited maximum allocation size.
if (size > wasm::kV8MaxWasmMemoryBytes) return false; if (size > wasm::max_mem_pages() * uint64_t{wasm::kWasmPageSize}) {
// Enforce flag-limited maximum allocation size.
if (size > (FLAG_wasm_max_mem_pages * uint64_t{wasm::kWasmPageSize})) {
return false; return false;
} }
// Enforce power-of-2 sizes for 2^12 - 2^24. // Enforce power-of-2 sizes for 2^12 - 2^24.
......
...@@ -58,9 +58,10 @@ struct CompilationEnv { ...@@ -58,9 +58,10 @@ struct CompilationEnv {
runtime_exception_support(runtime_exception_support), runtime_exception_support(runtime_exception_support),
min_memory_size(module ? module->initial_pages * uint64_t{kWasmPageSize} min_memory_size(module ? module->initial_pages * uint64_t{kWasmPageSize}
: 0), : 0),
max_memory_size(module && module->has_maximum_pages max_memory_size((module && module->has_maximum_pages
? (module->maximum_pages * uint64_t{kWasmPageSize}) ? module->maximum_pages
: kSpecMaxWasmMemoryBytes), : kV8MaxWasmMemoryPages) *
uint64_t{kWasmPageSize}),
lower_simd(lower_simd) {} lower_simd(lower_simd) {}
}; };
......
...@@ -1880,13 +1880,13 @@ void InstanceBuilder::InitGlobals() { ...@@ -1880,13 +1880,13 @@ void InstanceBuilder::InitGlobals() {
// Allocate memory for a module instance as a new JSArrayBuffer. // Allocate memory for a module instance as a new JSArrayBuffer.
Handle<JSArrayBuffer> InstanceBuilder::AllocateMemory(uint32_t num_pages) { Handle<JSArrayBuffer> InstanceBuilder::AllocateMemory(uint32_t num_pages) {
if (num_pages > FLAG_wasm_max_mem_pages) { if (num_pages > max_mem_pages()) {
thrower_->RangeError("Out of memory: wasm memory too large"); thrower_->RangeError("Out of memory: wasm memory too large");
return Handle<JSArrayBuffer>::null(); return Handle<JSArrayBuffer>::null();
} }
const bool is_shared_memory = module_->has_shared_memory && enabled_.threads; const bool is_shared_memory = module_->has_shared_memory && enabled_.threads;
i::SharedFlag shared_flag = SharedFlag shared_flag =
is_shared_memory ? i::SharedFlag::kShared : i::SharedFlag::kNotShared; is_shared_memory ? SharedFlag::kShared : SharedFlag::kNotShared;
Handle<JSArrayBuffer> mem_buffer; Handle<JSArrayBuffer> mem_buffer;
if (!NewArrayBuffer(isolate_, num_pages * kWasmPageSize, shared_flag) if (!NewArrayBuffer(isolate_, num_pages * kWasmPageSize, shared_flag)
.ToHandle(&mem_buffer)) { .ToHandle(&mem_buffer)) {
......
...@@ -520,7 +520,7 @@ class ModuleDecoderImpl : public Decoder { ...@@ -520,7 +520,7 @@ class ModuleDecoderImpl : public Decoder {
if (!AddMemory(module_.get())) break; if (!AddMemory(module_.get())) break;
uint8_t flags = validate_memory_flags(&module_->has_shared_memory); uint8_t flags = validate_memory_flags(&module_->has_shared_memory);
consume_resizable_limits( consume_resizable_limits(
"memory", "pages", FLAG_wasm_max_mem_pages, "memory", "pages", kSpecMaxWasmMemoryPages,
&module_->initial_pages, &module_->has_maximum_pages, &module_->initial_pages, &module_->has_maximum_pages,
kSpecMaxWasmMemoryPages, &module_->maximum_pages, flags); kSpecMaxWasmMemoryPages, &module_->maximum_pages, flags);
break; break;
...@@ -614,7 +614,7 @@ class ModuleDecoderImpl : public Decoder { ...@@ -614,7 +614,7 @@ class ModuleDecoderImpl : public Decoder {
if (!AddMemory(module_.get())) break; if (!AddMemory(module_.get())) break;
uint8_t flags = validate_memory_flags(&module_->has_shared_memory); uint8_t flags = validate_memory_flags(&module_->has_shared_memory);
consume_resizable_limits( consume_resizable_limits(
"memory", "pages", FLAG_wasm_max_mem_pages, &module_->initial_pages, "memory", "pages", kSpecMaxWasmMemoryPages, &module_->initial_pages,
&module_->has_maximum_pages, kSpecMaxWasmMemoryPages, &module_->has_maximum_pages, kSpecMaxWasmMemoryPages,
&module_->maximum_pages, flags); &module_->maximum_pages, flags);
} }
......
...@@ -317,6 +317,12 @@ std::shared_ptr<WasmEngine> WasmEngine::GetWasmEngine() { ...@@ -317,6 +317,12 @@ std::shared_ptr<WasmEngine> WasmEngine::GetWasmEngine() {
return std::shared_ptr<WasmEngine>(new WasmEngine()); return std::shared_ptr<WasmEngine>(new WasmEngine());
} }
// {max_mem_pages} is declared in wasm-limits.h.
uint32_t max_mem_pages() {
STATIC_ASSERT(kV8MaxWasmMemoryPages <= kMaxUInt32);
return std::min(uint32_t{kV8MaxWasmMemoryPages}, FLAG_wasm_max_mem_pages);
}
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -952,7 +952,7 @@ void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -952,7 +952,7 @@ void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
int64_t initial = 0; int64_t initial = 0;
if (!GetIntegerProperty(isolate, &thrower, context, descriptor, if (!GetIntegerProperty(isolate, &thrower, context, descriptor,
v8_str(isolate, "initial"), &initial, 0, v8_str(isolate, "initial"), &initial, 0,
i::FLAG_wasm_max_mem_pages)) { i::wasm::max_mem_pages())) {
return; return;
} }
// The descriptor's 'maximum'. // The descriptor's 'maximum'.
...@@ -1272,9 +1272,8 @@ void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -1272,9 +1272,8 @@ void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (!args[0]->IntegerValue(context).To(&delta_size)) return; if (!args[0]->IntegerValue(context).To(&delta_size)) return;
int64_t max_size64 = receiver->maximum_pages(); int64_t max_size64 = receiver->maximum_pages();
if (max_size64 < 0 || if (max_size64 < 0 || max_size64 > int64_t{i::wasm::max_mem_pages()}) {
max_size64 > static_cast<int64_t>(i::FLAG_wasm_max_mem_pages)) { max_size64 = i::wasm::max_mem_pages();
max_size64 = i::FLAG_wasm_max_mem_pages;
} }
i::Handle<i::JSArrayBuffer> old_buffer(receiver->array_buffer(), i_isolate); i::Handle<i::JSArrayBuffer> old_buffer(receiver->array_buffer(), i_isolate);
if (!old_buffer->is_growable()) { if (!old_buffer->is_growable()) {
......
...@@ -27,7 +27,7 @@ constexpr size_t kV8MaxWasmGlobals = 1000000; ...@@ -27,7 +27,7 @@ constexpr size_t kV8MaxWasmGlobals = 1000000;
constexpr size_t kV8MaxWasmExceptions = 1000000; constexpr size_t kV8MaxWasmExceptions = 1000000;
constexpr size_t kV8MaxWasmExceptionTypes = 1000000; constexpr size_t kV8MaxWasmExceptionTypes = 1000000;
constexpr size_t kV8MaxWasmDataSegments = 100000; constexpr size_t kV8MaxWasmDataSegments = 100000;
// Don't use this limit directly, but use the value of FLAG_wasm_max_mem_pages. // Don't use this limit directly, but use the value of {max_mem_pages()}.
constexpr size_t kV8MaxWasmMemoryPages = 32767; // = ~ 2 GiB constexpr size_t kV8MaxWasmMemoryPages = 32767; // = ~ 2 GiB
constexpr size_t kV8MaxWasmStringSize = 100000; constexpr size_t kV8MaxWasmStringSize = 100000;
constexpr size_t kV8MaxWasmModuleSize = 1024 * 1024 * 1024; // = 1 GiB constexpr size_t kV8MaxWasmModuleSize = 1024 * 1024 * 1024; // = 1 GiB
...@@ -46,17 +46,20 @@ static_assert(kV8MaxWasmMemoryPages <= kSpecMaxWasmMemoryPages, ...@@ -46,17 +46,20 @@ static_assert(kV8MaxWasmMemoryPages <= kSpecMaxWasmMemoryPages,
"v8 should not be more permissive than the spec"); "v8 should not be more permissive than the spec");
constexpr size_t kSpecMaxWasmTableSize = 0xFFFFFFFFu; constexpr size_t kSpecMaxWasmTableSize = 0xFFFFFFFFu;
constexpr uint64_t kV8MaxWasmMemoryBytes =
kV8MaxWasmMemoryPages * uint64_t{kWasmPageSize};
constexpr uint64_t kSpecMaxWasmMemoryBytes =
kSpecMaxWasmMemoryPages * uint64_t{kWasmPageSize};
constexpr uint64_t kWasmMaxHeapOffset = constexpr uint64_t kWasmMaxHeapOffset =
static_cast<uint64_t>( static_cast<uint64_t>(
std::numeric_limits<uint32_t>::max()) // maximum base value std::numeric_limits<uint32_t>::max()) // maximum base value
+ std::numeric_limits<uint32_t>::max(); // maximum index value + std::numeric_limits<uint32_t>::max(); // maximum index value
// Defined in wasm-engine.cc.
// TODO(wasm): Make this size_t for wasm64. Currently the --wasm-max-mem-pages
// flag is only uint32_t.
uint32_t max_mem_pages();
inline uint64_t max_mem_bytes() {
return uint64_t{max_mem_pages()} * kWasmPageSize;
}
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -277,10 +277,8 @@ Handle<JSArrayBuffer> SetupArrayBuffer(Isolate* isolate, void* backing_store, ...@@ -277,10 +277,8 @@ Handle<JSArrayBuffer> SetupArrayBuffer(Isolate* isolate, void* backing_store,
MaybeHandle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size, MaybeHandle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size,
SharedFlag shared) { SharedFlag shared) {
// Enforce engine-limited maximum allocation size.
if (size > kV8MaxWasmMemoryBytes) return {};
// Enforce flag-limited maximum allocation size. // Enforce flag-limited maximum allocation size.
if (size > (FLAG_wasm_max_mem_pages * uint64_t{kWasmPageSize})) return {}; if (size > max_mem_bytes()) return {};
WasmMemoryTracker* memory_tracker = isolate->wasm_engine()->memory_tracker(); WasmMemoryTracker* memory_tracker = isolate->wasm_engine()->memory_tracker();
......
...@@ -877,20 +877,20 @@ MaybeHandle<JSArrayBuffer> MemoryGrowBuffer(Isolate* isolate, ...@@ -877,20 +877,20 @@ MaybeHandle<JSArrayBuffer> MemoryGrowBuffer(Isolate* isolate,
Handle<JSArrayBuffer> old_buffer, Handle<JSArrayBuffer> old_buffer,
uint32_t pages, uint32_t pages,
uint32_t maximum_pages) { uint32_t maximum_pages) {
CHECK_GE(wasm::max_mem_pages(), maximum_pages);
if (!old_buffer->is_growable()) return {}; if (!old_buffer->is_growable()) return {};
void* old_mem_start = old_buffer->backing_store(); void* old_mem_start = old_buffer->backing_store();
size_t old_size = old_buffer->byte_length(); size_t old_size = old_buffer->byte_length();
CHECK_GE(wasm::kV8MaxWasmMemoryBytes, old_size);
CHECK_EQ(0, old_size % wasm::kWasmPageSize); CHECK_EQ(0, old_size % wasm::kWasmPageSize);
size_t old_pages = old_size / wasm::kWasmPageSize; size_t old_pages = old_size / wasm::kWasmPageSize;
if (old_pages > maximum_pages || // already reached maximum CHECK_GE(wasm::max_mem_pages(), old_pages);
(pages > maximum_pages - old_pages) || // exceeds remaining
(pages > FLAG_wasm_max_mem_pages - old_pages)) { // exceeds limit if ((pages > maximum_pages - old_pages) || // exceeds remaining
(pages > wasm::max_mem_pages() - old_pages)) { // exceeds limit
return {}; return {};
} }
size_t new_size = size_t new_size =
static_cast<size_t>(old_pages + pages) * wasm::kWasmPageSize; static_cast<size_t>(old_pages + pages) * wasm::kWasmPageSize;
CHECK_GE(wasm::kV8MaxWasmMemoryBytes, new_size);
// Reusing the backing store from externalized buffers causes problems with // Reusing the backing store from externalized buffers causes problems with
// Blink's array buffers. The connection between the two is lost, which can // Blink's array buffers. The connection between the two is lost, which can
...@@ -1050,10 +1050,10 @@ int32_t WasmMemoryObject::Grow(Isolate* isolate, ...@@ -1050,10 +1050,10 @@ int32_t WasmMemoryObject::Grow(Isolate* isolate,
DCHECK_EQ(0, old_size % wasm::kWasmPageSize); DCHECK_EQ(0, old_size % wasm::kWasmPageSize);
Handle<JSArrayBuffer> new_buffer; Handle<JSArrayBuffer> new_buffer;
uint32_t maximum_pages = FLAG_wasm_max_mem_pages; uint32_t maximum_pages = wasm::max_mem_pages();
if (memory_object->has_maximum_pages()) { if (memory_object->has_maximum_pages()) {
maximum_pages = Min(FLAG_wasm_max_mem_pages, maximum_pages = std::min(
static_cast<uint32_t>(memory_object->maximum_pages())); maximum_pages, static_cast<uint32_t>(memory_object->maximum_pages()));
} }
if (!MemoryGrowBuffer(isolate, old_buffer, pages, maximum_pages) if (!MemoryGrowBuffer(isolate, old_buffer, pages, maximum_pages)
.ToHandle(&new_buffer)) { .ToHandle(&new_buffer)) {
...@@ -1220,7 +1220,7 @@ bool WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize( ...@@ -1220,7 +1220,7 @@ bool WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
} }
void WasmInstanceObject::SetRawMemory(byte* mem_start, size_t mem_size) { void WasmInstanceObject::SetRawMemory(byte* mem_start, size_t mem_size) {
CHECK_LE(mem_size, wasm::kV8MaxWasmMemoryBytes); CHECK_LE(mem_size, wasm::max_mem_bytes());
#if V8_HOST_ARCH_64_BIT #if V8_HOST_ARCH_64_BIT
uint64_t mem_mask64 = base::bits::RoundUpToPowerOfTwo64(mem_size) - 1; uint64_t mem_mask64 = base::bits::RoundUpToPowerOfTwo64(mem_size) - 1;
set_memory_start(mem_start); set_memory_start(mem_start);
......
// 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: --wasm-max-mem-pages=49152
let mem = new WebAssembly.Memory({initial: 1});
try {
mem.grow(49151);
} catch (e) {
// This can fail on 32-bit systems if we cannot make such a big reservation.
if (!(e instanceof RangeError)) throw e;
}
...@@ -8,7 +8,8 @@ ...@@ -8,7 +8,8 @@
load("test/mjsunit/wasm/wasm-constants.js"); load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js"); load("test/mjsunit/wasm/wasm-module-builder.js");
function testHugeMemory() { (function testHugeMemory() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder(); var builder = new WasmModuleBuilder();
const num_pages = 49152; // 3GB const num_pages = 49152; // 3GB
...@@ -30,10 +31,10 @@ function testHugeMemory() { ...@@ -30,10 +31,10 @@ function testHugeMemory() {
assertEquals(0, geti(2500, 1 << 20)); assertEquals(0, geti(2500, 1 << 20));
print("Out of bounds"); print("Out of bounds");
assertTraps(kTrapMemOutOfBounds, () => geti(3500, 1 << 20)); assertTraps(kTrapMemOutOfBounds, () => geti(3500, 1 << 20));
} })();
testHugeMemory();
function testHugeMemoryConstInBounds() { (function testHugeMemoryConstInBounds() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder(); var builder = new WasmModuleBuilder();
const num_pages = 49152; // 3GB const num_pages = 49152; // 3GB
...@@ -51,10 +52,10 @@ function testHugeMemoryConstInBounds() { ...@@ -51,10 +52,10 @@ function testHugeMemoryConstInBounds() {
print("In bounds"); print("In bounds");
assertEquals(0, geti()); assertEquals(0, geti());
} })();
testHugeMemoryConstInBounds();
function testHugeMemoryConstOutOfBounds() { (function testHugeMemoryConstOutOfBounds() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder(); var builder = new WasmModuleBuilder();
const num_pages = 49152; // 3GB const num_pages = 49152; // 3GB
...@@ -72,5 +73,11 @@ function testHugeMemoryConstOutOfBounds() { ...@@ -72,5 +73,11 @@ function testHugeMemoryConstOutOfBounds() {
print("Out of bounds"); print("Out of bounds");
assertTraps(kTrapMemOutOfBounds, geti); assertTraps(kTrapMemOutOfBounds, geti);
} })();
testHugeMemoryConstOutOfBounds();
(function testGrowHugeMemory() {
print(arguments.callee.name);
let mem = new WebAssembly.Memory({initial: 1});
mem.grow(49151);
})();
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