Line data Source code
1 : // Copyright 2017 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "src/wasm/wasm-memory.h"
6 : #include "src/objects-inl.h"
7 : #include "src/wasm/wasm-limits.h"
8 : #include "src/wasm/wasm-module.h"
9 :
10 : namespace v8 {
11 : namespace internal {
12 : namespace wasm {
13 :
14 28219 : void* TryAllocateBackingStore(Isolate* isolate, size_t size,
15 : bool enable_guard_regions, void*& allocation_base,
16 : size_t& allocation_length) {
17 : // TODO(eholk): Right now enable_guard_regions has no effect on 32-bit
18 : // systems. It may be safer to fail instead, given that other code might do
19 : // things that would be unsafe if they expected guard pages where there
20 : // weren't any.
21 13764 : if (enable_guard_regions) {
22 : // TODO(eholk): On Windows we want to make sure we don't commit the guard
23 : // pages yet.
24 :
25 : // We always allocate the largest possible offset into the heap, so the
26 : // addressable memory after the guard page can be made inaccessible.
27 1382 : allocation_length = RoundUp(kWasmMaxHeapOffset, base::OS::CommitPageSize());
28 : DCHECK_EQ(0, size % base::OS::CommitPageSize());
29 :
30 : // AllocateGuarded makes the whole region inaccessible by default.
31 : allocation_base =
32 691 : isolate->array_buffer_allocator()->Reserve(allocation_length);
33 691 : if (allocation_base == nullptr) {
34 : return nullptr;
35 : }
36 :
37 : void* memory = allocation_base;
38 :
39 : // Make the part we care about accessible.
40 : isolate->array_buffer_allocator()->SetProtection(
41 691 : memory, size, v8::ArrayBuffer::Allocator::Protection::kReadWrite);
42 :
43 : reinterpret_cast<v8::Isolate*>(isolate)
44 691 : ->AdjustAmountOfExternalAllocatedMemory(size);
45 :
46 691 : return memory;
47 : } else {
48 : void* memory =
49 26146 : size == 0 ? nullptr : isolate->array_buffer_allocator()->Allocate(size);
50 13073 : allocation_base = memory;
51 13073 : allocation_length = size;
52 13073 : return memory;
53 : }
54 : }
55 :
56 19190 : Handle<JSArrayBuffer> SetupArrayBuffer(Isolate* isolate, void* allocation_base,
57 : size_t allocation_length,
58 : void* backing_store, size_t size,
59 : bool is_external,
60 : bool enable_guard_regions,
61 : SharedFlag shared) {
62 : Handle<JSArrayBuffer> buffer =
63 19190 : isolate->factory()->NewJSArrayBuffer(shared, TENURED);
64 : DCHECK_GE(kMaxInt, size);
65 : if (shared == SharedFlag::kShared) DCHECK(FLAG_experimental_wasm_threads);
66 : JSArrayBuffer::Setup(buffer, isolate, is_external, allocation_base,
67 : allocation_length, backing_store, static_cast<int>(size),
68 19190 : shared);
69 : buffer->set_is_neuterable(false);
70 : buffer->set_is_wasm_buffer(true);
71 : buffer->set_has_guard_region(enable_guard_regions);
72 19190 : return buffer;
73 : }
74 :
75 13972 : Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size,
76 : bool enable_guard_regions,
77 : SharedFlag shared) {
78 : // Check against kMaxInt, since the byte length is stored as int in the
79 : // JSArrayBuffer. Note that wasm_max_mem_pages can be raised from the command
80 : // line, and we don't want to fail a CHECK then.
81 13972 : if (size > FLAG_wasm_max_mem_pages * WasmModule::kPageSize ||
82 : size > kMaxInt) {
83 : // TODO(titzer): lift restriction on maximum memory allocated here.
84 : return Handle<JSArrayBuffer>::null();
85 : }
86 :
87 13962 : void* allocation_base = nullptr; // Set by TryAllocateBackingStore
88 13962 : size_t allocation_length = 0; // Set by TryAllocateBackingStore
89 : // Do not reserve memory till non zero memory is encountered.
90 : void* memory =
91 : (size == 0) ? nullptr
92 : : TryAllocateBackingStore(isolate, size, enable_guard_regions,
93 13962 : allocation_base, allocation_length);
94 :
95 13962 : if (size > 0 && memory == nullptr) {
96 : return Handle<JSArrayBuffer>::null();
97 : }
98 :
99 : #if DEBUG
100 : // Double check the API allocator actually zero-initialized the memory.
101 : const byte* bytes = reinterpret_cast<const byte*>(memory);
102 : for (size_t i = 0; i < size; ++i) {
103 : DCHECK_EQ(0, bytes[i]);
104 : }
105 : #endif
106 :
107 : constexpr bool is_external = false;
108 : return SetupArrayBuffer(isolate, allocation_base, allocation_length, memory,
109 13962 : size, is_external, enable_guard_regions, shared);
110 : }
111 :
112 348 : void DetachMemoryBuffer(Isolate* isolate, Handle<JSArrayBuffer> buffer,
113 : bool free_memory) {
114 : const bool is_external = buffer->is_external();
115 : DCHECK(!buffer->is_neuterable());
116 348 : if (!is_external) {
117 : buffer->set_is_external(true);
118 330 : isolate->heap()->UnregisterArrayBuffer(*buffer);
119 330 : if (free_memory) {
120 : // We need to free the memory before neutering the buffer because
121 : // FreeBackingStore reads buffer->allocation_base(), which is nulled out
122 : // by Neuter. This means there is a dangling pointer until we neuter the
123 : // buffer. Since there is no way for the user to directly call
124 : // FreeBackingStore, we can ensure this is safe.
125 234 : buffer->FreeBackingStore();
126 : }
127 : }
128 : buffer->set_is_neuterable(true);
129 348 : buffer->Neuter();
130 348 : }
131 :
132 : } // namespace wasm
133 : } // namespace internal
134 : } // namespace v8
|