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 {
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})) {
// Enforce engine-limited and flag-limited maximum allocation size.
if (size > wasm::max_mem_pages() * uint64_t{wasm::kWasmPageSize}) {
return false;
}
// Enforce power-of-2 sizes for 2^12 - 2^24.
......
......@@ -58,9 +58,10 @@ struct CompilationEnv {
runtime_exception_support(runtime_exception_support),
min_memory_size(module ? module->initial_pages * uint64_t{kWasmPageSize}
: 0),
max_memory_size(module && module->has_maximum_pages
? (module->maximum_pages * uint64_t{kWasmPageSize})
: kSpecMaxWasmMemoryBytes),
max_memory_size((module && module->has_maximum_pages
? module->maximum_pages
: kV8MaxWasmMemoryPages) *
uint64_t{kWasmPageSize}),
lower_simd(lower_simd) {}
};
......
......@@ -1880,13 +1880,13 @@ void InstanceBuilder::InitGlobals() {
// Allocate memory for a module instance as a new JSArrayBuffer.
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");
return Handle<JSArrayBuffer>::null();
}
const bool is_shared_memory = module_->has_shared_memory && enabled_.threads;
i::SharedFlag shared_flag =
is_shared_memory ? i::SharedFlag::kShared : i::SharedFlag::kNotShared;
SharedFlag shared_flag =
is_shared_memory ? SharedFlag::kShared : SharedFlag::kNotShared;
Handle<JSArrayBuffer> mem_buffer;
if (!NewArrayBuffer(isolate_, num_pages * kWasmPageSize, shared_flag)
.ToHandle(&mem_buffer)) {
......
......@@ -520,7 +520,7 @@ class ModuleDecoderImpl : public Decoder {
if (!AddMemory(module_.get())) break;
uint8_t flags = validate_memory_flags(&module_->has_shared_memory);
consume_resizable_limits(
"memory", "pages", FLAG_wasm_max_mem_pages,
"memory", "pages", kSpecMaxWasmMemoryPages,
&module_->initial_pages, &module_->has_maximum_pages,
kSpecMaxWasmMemoryPages, &module_->maximum_pages, flags);
break;
......@@ -614,7 +614,7 @@ class ModuleDecoderImpl : public Decoder {
if (!AddMemory(module_.get())) break;
uint8_t flags = validate_memory_flags(&module_->has_shared_memory);
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_->maximum_pages, flags);
}
......
......@@ -317,6 +317,12 @@ std::shared_ptr<WasmEngine> WasmEngine::GetWasmEngine() {
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 internal
} // namespace v8
......@@ -952,7 +952,7 @@ void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
int64_t initial = 0;
if (!GetIntegerProperty(isolate, &thrower, context, descriptor,
v8_str(isolate, "initial"), &initial, 0,
i::FLAG_wasm_max_mem_pages)) {
i::wasm::max_mem_pages())) {
return;
}
// The descriptor's 'maximum'.
......@@ -1272,9 +1272,8 @@ void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (!args[0]->IntegerValue(context).To(&delta_size)) return;
int64_t max_size64 = receiver->maximum_pages();
if (max_size64 < 0 ||
max_size64 > static_cast<int64_t>(i::FLAG_wasm_max_mem_pages)) {
max_size64 = i::FLAG_wasm_max_mem_pages;
if (max_size64 < 0 || max_size64 > int64_t{i::wasm::max_mem_pages()}) {
max_size64 = i::wasm::max_mem_pages();
}
i::Handle<i::JSArrayBuffer> old_buffer(receiver->array_buffer(), i_isolate);
if (!old_buffer->is_growable()) {
......
......@@ -27,7 +27,7 @@ constexpr size_t kV8MaxWasmGlobals = 1000000;
constexpr size_t kV8MaxWasmExceptions = 1000000;
constexpr size_t kV8MaxWasmExceptionTypes = 1000000;
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 kV8MaxWasmStringSize = 100000;
constexpr size_t kV8MaxWasmModuleSize = 1024 * 1024 * 1024; // = 1 GiB
......@@ -46,17 +46,20 @@ static_assert(kV8MaxWasmMemoryPages <= kSpecMaxWasmMemoryPages,
"v8 should not be more permissive than the spec");
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 =
static_cast<uint64_t>(
std::numeric_limits<uint32_t>::max()) // maximum base 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 internal
} // namespace v8
......
......@@ -277,10 +277,8 @@ Handle<JSArrayBuffer> SetupArrayBuffer(Isolate* isolate, void* backing_store,
MaybeHandle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size,
SharedFlag shared) {
// Enforce engine-limited maximum allocation size.
if (size > kV8MaxWasmMemoryBytes) return {};
// 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();
......
......@@ -877,20 +877,20 @@ MaybeHandle<JSArrayBuffer> MemoryGrowBuffer(Isolate* isolate,
Handle<JSArrayBuffer> old_buffer,
uint32_t pages,
uint32_t maximum_pages) {
CHECK_GE(wasm::max_mem_pages(), maximum_pages);
if (!old_buffer->is_growable()) return {};
void* old_mem_start = old_buffer->backing_store();
size_t old_size = old_buffer->byte_length();
CHECK_GE(wasm::kV8MaxWasmMemoryBytes, old_size);
CHECK_EQ(0, old_size % wasm::kWasmPageSize);
size_t old_pages = old_size / wasm::kWasmPageSize;
if (old_pages > maximum_pages || // already reached maximum
(pages > maximum_pages - old_pages) || // exceeds remaining
(pages > FLAG_wasm_max_mem_pages - old_pages)) { // exceeds limit
CHECK_GE(wasm::max_mem_pages(), old_pages);
if ((pages > maximum_pages - old_pages) || // exceeds remaining
(pages > wasm::max_mem_pages() - old_pages)) { // exceeds limit
return {};
}
size_t new_size =
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
// Blink's array buffers. The connection between the two is lost, which can
......@@ -1050,10 +1050,10 @@ int32_t WasmMemoryObject::Grow(Isolate* isolate,
DCHECK_EQ(0, old_size % wasm::kWasmPageSize);
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()) {
maximum_pages = Min(FLAG_wasm_max_mem_pages,
static_cast<uint32_t>(memory_object->maximum_pages()));
maximum_pages = std::min(
maximum_pages, static_cast<uint32_t>(memory_object->maximum_pages()));
}
if (!MemoryGrowBuffer(isolate, old_buffer, pages, maximum_pages)
.ToHandle(&new_buffer)) {
......@@ -1220,7 +1220,7 @@ bool WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
}
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
uint64_t mem_mask64 = base::bits::RoundUpToPowerOfTwo64(mem_size) - 1;
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 @@
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
function testHugeMemory() {
(function testHugeMemory() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
const num_pages = 49152; // 3GB
......@@ -30,10 +31,10 @@ function testHugeMemory() {
assertEquals(0, geti(2500, 1 << 20));
print("Out of bounds");
assertTraps(kTrapMemOutOfBounds, () => geti(3500, 1 << 20));
}
testHugeMemory();
})();
function testHugeMemoryConstInBounds() {
(function testHugeMemoryConstInBounds() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
const num_pages = 49152; // 3GB
......@@ -51,10 +52,10 @@ function testHugeMemoryConstInBounds() {
print("In bounds");
assertEquals(0, geti());
}
testHugeMemoryConstInBounds();
})();
function testHugeMemoryConstOutOfBounds() {
(function testHugeMemoryConstOutOfBounds() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
const num_pages = 49152; // 3GB
......@@ -72,5 +73,11 @@ function testHugeMemoryConstOutOfBounds() {
print("Out of bounds");
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