1// Copyright 2018 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/init/isolate-allocator.h"
6
7#include "src/base/bounded-page-allocator.h"
8#include "src/execution/isolate.h"
9#include "src/heap/code-range.h"
10#include "src/sandbox/sandbox.h"
11#include "src/utils/memcopy.h"
12#include "src/utils/utils.h"
13
14namespace v8 {
15namespace internal {
16
17#ifdef V8_COMPRESS_POINTERS
18namespace {
19
20// "IsolateRootBiasPage" is an optional region before the 4Gb aligned
21// reservation. This "IsolateRootBiasPage" page is supposed to be used for
22// storing part of the Isolate object when Isolate::isolate_root_bias() is
23// not zero.
24inline size_t GetIsolateRootBiasPageSize(
25    v8::PageAllocator* platform_page_allocator) {
26  return RoundUp(Isolate::isolate_root_bias(),
27                 platform_page_allocator->AllocatePageSize());
28}
29
30}  // namespace
31
32struct PtrComprCageReservationParams
33    : public VirtualMemoryCage::ReservationParams {
34  PtrComprCageReservationParams() {
35    page_allocator = GetPlatformPageAllocator();
36
37    // This is only used when there is a per-Isolate cage, in which case the
38    // Isolate is allocated within the cage, and the Isolate root is also the
39    // cage base.
40    const size_t kIsolateRootBiasPageSize =
41        COMPRESS_POINTERS_IN_ISOLATE_CAGE_BOOL
42            ? GetIsolateRootBiasPageSize(page_allocator)
43            : 0;
44    reservation_size = kPtrComprCageReservationSize + kIsolateRootBiasPageSize;
45    base_alignment = kPtrComprCageBaseAlignment;
46    base_bias_size = kIsolateRootBiasPageSize;
47
48    // Simplify BoundedPageAllocator's life by configuring it to use same page
49    // size as the Heap will use (MemoryChunk::kPageSize).
50    page_size =
51        RoundUp(size_t{1} << kPageSizeBits, page_allocator->AllocatePageSize());
52    requested_start_hint =
53        reinterpret_cast<Address>(page_allocator->GetRandomMmapAddr());
54  }
55};
56#endif  // V8_COMPRESS_POINTERS
57
58#ifdef V8_COMPRESS_POINTERS_IN_SHARED_CAGE
59namespace {
60DEFINE_LAZY_LEAKY_OBJECT_GETTER(VirtualMemoryCage, GetProcessWidePtrComprCage)
61}  // anonymous namespace
62
63// static
64void IsolateAllocator::FreeProcessWidePtrComprCageForTesting() {
65  if (std::shared_ptr<CodeRange> code_range =
66          CodeRange::GetProcessWideCodeRange()) {
67    code_range->Free();
68  }
69  GetProcessWidePtrComprCage()->Free();
70}
71#endif  // V8_COMPRESS_POINTERS_IN_SHARED_CAGE
72
73// static
74void IsolateAllocator::InitializeOncePerProcess() {
75#ifdef V8_COMPRESS_POINTERS_IN_SHARED_CAGE
76  PtrComprCageReservationParams params;
77  base::AddressRegion existing_reservation;
78#ifdef V8_SANDBOX
79  // For now, we allow the sandbox to be disabled even when compiling with
80  // v8_enable_sandbox. This fallback will be disallowed in the future, at the
81  // latest once sandboxed pointers are enabled.
82  if (GetProcessWideSandbox()->is_disabled()) {
83    CHECK(kAllowBackingStoresOutsideSandbox);
84  } else {
85    auto sandbox = GetProcessWideSandbox();
86    CHECK(sandbox->is_initialized());
87    // The pointer compression cage must be placed at the start of the sandbox.
88
89    // TODO(chromium:12180) this currently assumes that no other pages were
90    // allocated through the cage's page allocator in the meantime. In the
91    // future, the cage initialization will happen just before this function
92    // runs, and so this will be guaranteed. Currently however, it is possible
93    // that the embedder accidentally uses the cage's page allocator prior to
94    // initializing V8, in which case this CHECK will likely fail.
95    Address base = sandbox->address_space()->AllocatePages(
96        sandbox->base(), params.reservation_size, params.base_alignment,
97        PagePermissions::kNoAccess);
98    CHECK_EQ(sandbox->base(), base);
99    existing_reservation = base::AddressRegion(base, params.reservation_size);
100    params.page_allocator = sandbox->page_allocator();
101  }
102#endif
103  if (!GetProcessWidePtrComprCage()->InitReservation(params,
104                                                     existing_reservation)) {
105    V8::FatalProcessOutOfMemory(
106        nullptr,
107        "Failed to reserve virtual memory for process-wide V8 "
108        "pointer compression cage");
109  }
110#endif
111}
112
113IsolateAllocator::IsolateAllocator() {
114#if defined(V8_COMPRESS_POINTERS_IN_ISOLATE_CAGE)
115  PtrComprCageReservationParams params;
116  if (!isolate_ptr_compr_cage_.InitReservation(params)) {
117    V8::FatalProcessOutOfMemory(
118        nullptr,
119        "Failed to reserve memory for Isolate V8 pointer compression cage");
120  }
121  page_allocator_ = isolate_ptr_compr_cage_.page_allocator();
122  CommitPagesForIsolate();
123#elif defined(V8_COMPRESS_POINTERS_IN_SHARED_CAGE)
124  // Allocate Isolate in C++ heap when sharing a cage.
125  CHECK(GetProcessWidePtrComprCage()->IsReserved());
126  page_allocator_ = GetProcessWidePtrComprCage()->page_allocator();
127  isolate_memory_ = ::operator new(sizeof(Isolate));
128#else
129  // Allocate Isolate in C++ heap.
130  page_allocator_ = GetPlatformPageAllocator();
131  isolate_memory_ = ::operator new(sizeof(Isolate));
132#endif  // V8_COMPRESS_POINTERS
133
134  CHECK_NOT_NULL(page_allocator_);
135}
136
137IsolateAllocator::~IsolateAllocator() {
138#ifdef V8_COMPRESS_POINTERS_IN_ISOLATE_CAGE
139  if (isolate_ptr_compr_cage_.reservation()->IsReserved()) {
140    // The actual memory will be freed when the |isolate_ptr_compr_cage_| will
141    // die.
142    return;
143  }
144#endif
145
146  // The memory was allocated in C++ heap.
147  ::operator delete(isolate_memory_);
148}
149
150VirtualMemoryCage* IsolateAllocator::GetPtrComprCage() {
151#if defined V8_COMPRESS_POINTERS_IN_ISOLATE_CAGE
152  return &isolate_ptr_compr_cage_;
153#elif defined V8_COMPRESS_POINTERS_IN_SHARED_CAGE
154  return GetProcessWidePtrComprCage();
155#else
156  return nullptr;
157#endif
158}
159
160const VirtualMemoryCage* IsolateAllocator::GetPtrComprCage() const {
161  return const_cast<IsolateAllocator*>(this)->GetPtrComprCage();
162}
163
164#ifdef V8_COMPRESS_POINTERS_IN_ISOLATE_CAGE
165void IsolateAllocator::CommitPagesForIsolate() {
166  v8::PageAllocator* platform_page_allocator = GetPlatformPageAllocator();
167
168  CHECK(isolate_ptr_compr_cage_.IsReserved());
169  Address isolate_root = isolate_ptr_compr_cage_.base();
170  CHECK(IsAligned(isolate_root, kPtrComprCageBaseAlignment));
171  CHECK_GE(isolate_ptr_compr_cage_.reservation()->size(),
172           kPtrComprCageReservationSize +
173               GetIsolateRootBiasPageSize(platform_page_allocator));
174  CHECK(isolate_ptr_compr_cage_.reservation()->InVM(
175      isolate_root, kPtrComprCageReservationSize));
176
177  size_t page_size = page_allocator_->AllocatePageSize();
178  Address isolate_address = isolate_root - Isolate::isolate_root_bias();
179  Address isolate_end = isolate_address + sizeof(Isolate);
180
181  // Inform the bounded page allocator about reserved pages.
182  {
183    Address reserved_region_address = isolate_root;
184    size_t reserved_region_size =
185        RoundUp(isolate_end, page_size) - reserved_region_address;
186
187    CHECK(isolate_ptr_compr_cage_.page_allocator()->AllocatePagesAt(
188        reserved_region_address, reserved_region_size,
189        PageAllocator::Permission::kNoAccess));
190  }
191
192  // Commit pages where the Isolate will be stored.
193  {
194    size_t commit_page_size = platform_page_allocator->CommitPageSize();
195    Address committed_region_address =
196        RoundDown(isolate_address, commit_page_size);
197    size_t committed_region_size =
198        RoundUp(isolate_end, commit_page_size) - committed_region_address;
199
200    // We are using |isolate_ptr_compr_cage_.reservation()| directly here
201    // because |page_allocator_| has bigger commit page size than we actually
202    // need.
203    CHECK(isolate_ptr_compr_cage_.reservation()->SetPermissions(
204        committed_region_address, committed_region_size,
205        PageAllocator::kReadWrite));
206
207    if (Heap::ShouldZapGarbage()) {
208      MemsetPointer(reinterpret_cast<Address*>(committed_region_address),
209                    kZapValue, committed_region_size / kSystemPointerSize);
210    }
211  }
212  isolate_memory_ = reinterpret_cast<void*>(isolate_address);
213}
214#endif  // V8_COMPRESS_POINTERS_IN_ISOLATE_CAGE
215
216}  // namespace internal
217}  // namespace v8
218