Commit 2aecded2 authored by Deepti Gandluri's avatar Deepti Gandluri Committed by Commit Bot

[wasm] Memory.Grow with guard pages enabled should adjust memory allocated

 - Memory.Grow with guard pages enabled should adjust amount of allocated
   memory, and not allocate a new buffer. This was disabled because previously
   the backing store was freed in the MemoryFinalizer, and we needed to be sure
   that the backing store is not released till the last buffer using it is
   released. This is now safe as we no longer use the MemoryFinalizer
 - SetProtection should use Guard/Unprotect that use mprotect underneath,
   instead of CommitRegion/UncommitRegion that use mmap
 - Move buffer allocation to the end to avoid inconsistent memory due to GC

BUG=v8:5886

Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng
Change-Id: I0d7edb884bd1e3167eb5fbced6953c6401688d40
Reviewed-on: https://chromium-review.googlesource.com/629517Reviewed-by: 's avatarBrad Nelson <bradnelson@chromium.org>
Reviewed-by: 's avatarEric Holk <eholk@chromium.org>
Commit-Queue: Deepti Gandluri <gdeepti@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47960}
parent dbf2fcef
......@@ -504,12 +504,11 @@ class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
v8::ArrayBuffer::Allocator::Protection protection) {
switch (protection) {
case v8::ArrayBuffer::Allocator::Protection::kNoAccess: {
base::VirtualMemory::UncommitRegion(data, length);
base::OS::Guard(data, length);
return;
}
case v8::ArrayBuffer::Allocator::Protection::kReadWrite: {
const bool is_executable = false;
base::VirtualMemory::CommitRegion(data, length, is_executable);
base::OS::Unprotect(data, length);
return;
}
}
......
......@@ -828,7 +828,6 @@ void OS::Guard(void* address, const size_t size) {
void OS::Unprotect(void* address, const size_t size) {
LPVOID result = VirtualAlloc(address, size, MEM_COMMIT, PAGE_READWRITE);
DCHECK_IMPLIES(result != nullptr, GetLastError() == 0);
USE(result);
}
......
......@@ -764,8 +764,16 @@ void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
thrower.RangeError("Unable to grow instance memory.");
return;
}
bool free_memory = (delta_size != 0);
if (!old_buffer->is_shared()) {
// When delta_size == 0, or guard pages are enabled, the same backing store
// is used. To be spec compliant, the buffer associated with the memory
// object needs to be detached. Setup a new buffer with the same backing
// store, detach the old buffer, and do not free backing store memory.
bool free_memory = delta_size != 0 && !old_buffer->has_guard_region();
if ((!free_memory && old_size != 0) || new_size64 == 0) {
i::WasmMemoryObject::SetupNewBufferWithSameBackingStore(
i_isolate, receiver, static_cast<uint32_t>(new_size64));
}
i::wasm::DetachWebAssemblyMemoryBuffer(i_isolate, old_buffer, free_memory);
}
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
......
......@@ -251,7 +251,8 @@ Handle<JSArrayBuffer> SetupArrayBuffer(Isolate* isolate, void* allocation_base,
bool is_external,
bool enable_guard_regions,
SharedFlag shared) {
Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer(shared);
Handle<JSArrayBuffer> buffer =
isolate->factory()->NewJSArrayBuffer(shared, TENURED);
DCHECK_GE(kMaxInt, size);
if (shared == SharedFlag::kShared) DCHECK(FLAG_experimental_wasm_threads);
JSArrayBuffer::Setup(buffer, isolate, is_external, allocation_base,
......
......@@ -292,20 +292,33 @@ Handle<JSArrayBuffer> GrowMemoryBuffer(Isolate* isolate,
if (old_pages > maximum_pages || pages > maximum_pages - old_pages) {
return Handle<JSArrayBuffer>::null();
}
// TODO(gdeepti): Change the protection here instead of allocating a new
// buffer before guard regions are turned on, see issue #5886.
const bool enable_guard_regions = old_buffer.is_null()
? wasm::EnableGuardRegions()
: old_buffer->has_guard_region();
size_t new_size =
static_cast<size_t>(old_pages + pages) * WasmModule::kPageSize;
Handle<JSArrayBuffer> new_buffer =
wasm::NewArrayBuffer(isolate, new_size, enable_guard_regions);
if (new_buffer.is_null()) return new_buffer;
Address new_mem_start = static_cast<Address>(new_buffer->backing_store());
memcpy(new_mem_start, old_mem_start, old_size);
return new_buffer;
if (enable_guard_regions && old_size != 0) {
DCHECK(old_buffer->backing_store() != nullptr);
if (new_size > FLAG_wasm_max_mem_pages * WasmModule::kPageSize ||
new_size > kMaxInt) {
return Handle<JSArrayBuffer>::null();
}
isolate->array_buffer_allocator()->SetProtection(
old_mem_start, new_size,
v8::ArrayBuffer::Allocator::Protection::kReadWrite);
reinterpret_cast<v8::Isolate*>(isolate)
->AdjustAmountOfExternalAllocatedMemory(pages * WasmModule::kPageSize);
Handle<Object> length_obj = isolate->factory()->NewNumberFromSize(new_size);
old_buffer->set_byte_length(*length_obj);
return old_buffer;
} else {
Handle<JSArrayBuffer> new_buffer;
new_buffer = wasm::NewArrayBuffer(isolate, new_size, enable_guard_regions);
if (new_buffer.is_null() || old_size == 0) return new_buffer;
Address new_mem_start = static_cast<Address>(new_buffer->backing_store());
memcpy(new_mem_start, old_mem_start, old_size);
return new_buffer;
}
}
// May GC, because SetSpecializationMemInfoFrom may GC
......@@ -379,6 +392,32 @@ void WasmMemoryObject::RemoveInstance(Isolate* isolate,
}
}
void WasmMemoryObject::SetupNewBufferWithSameBackingStore(
Isolate* isolate, Handle<WasmMemoryObject> memory_object, uint32_t size) {
// In case of Memory.Grow(0), or Memory.Grow(delta) with guard pages enabled,
// Setup a new buffer, update memory object, and instances associated with the
// memory object, as the current buffer will be detached.
Handle<JSArrayBuffer> old_buffer(memory_object->array_buffer());
Handle<JSArrayBuffer> new_buffer;
constexpr bool is_external = false;
new_buffer = wasm::SetupArrayBuffer(
isolate, old_buffer->allocation_base(), old_buffer->allocation_length(),
old_buffer->backing_store(), size * WasmModule::kPageSize, is_external,
old_buffer->has_guard_region());
if (memory_object->has_instances()) {
Handle<WeakFixedArray> instances(memory_object->instances(), isolate);
for (int i = 0; i < instances->Length(); i++) {
Object* elem = instances->Get(i);
if (!elem->IsWasmInstanceObject()) continue;
Handle<WasmInstanceObject> instance(WasmInstanceObject::cast(elem),
isolate);
SetInstanceMemory(isolate, instance, new_buffer);
}
}
memory_object->set_array_buffer(*new_buffer);
}
// static
int32_t WasmMemoryObject::Grow(Isolate* isolate,
Handle<WasmMemoryObject> memory_object,
......@@ -389,13 +428,6 @@ int32_t WasmMemoryObject::Grow(Isolate* isolate,
Handle<JSArrayBuffer> new_buffer;
// Return current size if grow by 0.
if (pages == 0) {
// Even for pages == 0, we need to attach a new JSArrayBuffer with the same
// backing store and neuter the old one to be spec compliant.
new_buffer = wasm::SetupArrayBuffer(
isolate, old_buffer->allocation_base(), old_buffer->allocation_length(),
old_buffer->backing_store(), old_size, old_buffer->is_external(),
old_buffer->has_guard_region());
memory_object->set_array_buffer(*new_buffer);
DCHECK_EQ(0, old_size % WasmModule::kPageSize);
return old_size / WasmModule::kPageSize;
}
......@@ -422,7 +454,6 @@ int32_t WasmMemoryObject::Grow(Isolate* isolate,
UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size);
}
}
memory_object->set_array_buffer(*new_buffer);
DCHECK_EQ(0, old_size % WasmModule::kPageSize);
return old_size / WasmModule::kPageSize;
......
......@@ -137,6 +137,8 @@ class WasmMemoryObject : public JSObject {
int32_t maximum);
static int32_t Grow(Isolate*, Handle<WasmMemoryObject>, uint32_t pages);
static void SetupNewBufferWithSameBackingStore(
Isolate* isolate, Handle<WasmMemoryObject> memory_object, uint32_t size);
};
// A WebAssembly.Instance JavaScript-level object.
......
......@@ -1105,7 +1105,13 @@ TEST(Run_WasmModule_Buffer_Externalized_GrowMem) {
v8::Utils::ToLocal(memory)->Externalize();
uint32_t result = WasmMemoryObject::Grow(isolate, mem_obj, 4);
const bool free_memory = true;
bool free_memory = !memory->has_guard_region();
if (!free_memory) {
// current_pages = Initial memory size(16) + GrowWebAssemblyMemory(4)
const uint32_t current_pages = 20;
i::WasmMemoryObject::SetupNewBufferWithSameBackingStore(isolate, mem_obj,
current_pages);
}
wasm::DetachWebAssemblyMemoryBuffer(isolate, memory, free_memory);
CHECK_EQ(16, result);
memory = handle(mem_obj->array_buffer());
......@@ -1122,8 +1128,12 @@ TEST(Run_WasmModule_Buffer_Externalized_GrowMem) {
isolate->array_buffer_allocator()->Free(memory->allocation_base(),
memory->allocation_length(),
allocation_mode);
isolate->array_buffer_allocator()->Free(
old_allocation_base, old_allocation_length, allocation_mode);
if (free_memory) {
// GrowMemory without guard pages enabled allocates an extra buffer,
// that needs to be freed as well
isolate->array_buffer_allocator()->Free(
old_allocation_base, old_allocation_length, allocation_mode);
}
memory->set_allocation_base(nullptr);
memory->set_allocation_length(0);
}
......
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