• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 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/base/virtual-address-space.h"
6 
7 #include "include/v8-platform.h"
8 #include "src/base/bits.h"
9 #include "src/base/platform/platform.h"
10 #include "src/base/platform/wrappers.h"
11 
12 namespace v8 {
13 namespace base {
14 
15 #define STATIC_ASSERT_ENUM(a, b)                            \
16   static_assert(static_cast<int>(a) == static_cast<int>(b), \
17                 "mismatching enum: " #a)
18 
19 STATIC_ASSERT_ENUM(PagePermissions::kNoAccess, OS::MemoryPermission::kNoAccess);
20 STATIC_ASSERT_ENUM(PagePermissions::kReadWrite,
21                    OS::MemoryPermission::kReadWrite);
22 STATIC_ASSERT_ENUM(PagePermissions::kReadWriteExecute,
23                    OS::MemoryPermission::kReadWriteExecute);
24 STATIC_ASSERT_ENUM(PagePermissions::kReadExecute,
25                    OS::MemoryPermission::kReadExecute);
26 
27 #undef STATIC_ASSERT_ENUM
28 
29 namespace {
PagePermissionsToBitset(PagePermissions permissions)30 uint8_t PagePermissionsToBitset(PagePermissions permissions) {
31   switch (permissions) {
32     case PagePermissions::kNoAccess:
33       return 0b000;
34     case PagePermissions::kRead:
35       return 0b100;
36     case PagePermissions::kReadWrite:
37       return 0b110;
38     case PagePermissions::kReadWriteExecute:
39       return 0b111;
40     case PagePermissions::kReadExecute:
41       return 0b101;
42   }
43 }
44 }  // namespace
45 
IsSubset(PagePermissions lhs,PagePermissions rhs)46 bool IsSubset(PagePermissions lhs, PagePermissions rhs) {
47   uint8_t lhs_bits = PagePermissionsToBitset(lhs);
48   uint8_t rhs_bits = PagePermissionsToBitset(rhs);
49   return (lhs_bits & rhs_bits) == lhs_bits;
50 }
51 
VirtualAddressSpace()52 VirtualAddressSpace::VirtualAddressSpace()
53     : VirtualAddressSpaceBase(OS::CommitPageSize(), OS::AllocatePageSize(),
54                               kNullAddress,
55                               std::numeric_limits<uintptr_t>::max(),
56                               PagePermissions::kReadWriteExecute) {
57 #if V8_OS_WIN
58   // On Windows, this additional step is required to lookup the VirtualAlloc2
59   // and friends functions.
60   OS::EnsureWin32MemoryAPILoaded();
61 #endif  // V8_OS_WIN
62   DCHECK(bits::IsPowerOfTwo(page_size()));
63   DCHECK(bits::IsPowerOfTwo(allocation_granularity()));
64   DCHECK_GE(allocation_granularity(), page_size());
65   DCHECK(IsAligned(allocation_granularity(), page_size()));
66 }
67 
SetRandomSeed(int64_t seed)68 void VirtualAddressSpace::SetRandomSeed(int64_t seed) {
69   OS::SetRandomMmapSeed(seed);
70 }
71 
RandomPageAddress()72 Address VirtualAddressSpace::RandomPageAddress() {
73   return reinterpret_cast<Address>(OS::GetRandomMmapAddr());
74 }
75 
AllocatePages(Address hint,size_t size,size_t alignment,PagePermissions permissions)76 Address VirtualAddressSpace::AllocatePages(Address hint, size_t size,
77                                            size_t alignment,
78                                            PagePermissions permissions) {
79   DCHECK(IsAligned(alignment, allocation_granularity()));
80   DCHECK(IsAligned(hint, alignment));
81   DCHECK(IsAligned(size, allocation_granularity()));
82 
83   return reinterpret_cast<Address>(
84       OS::Allocate(reinterpret_cast<void*>(hint), size, alignment,
85                    static_cast<OS::MemoryPermission>(permissions)));
86 }
87 
FreePages(Address address,size_t size)88 void VirtualAddressSpace::FreePages(Address address, size_t size) {
89   DCHECK(IsAligned(address, allocation_granularity()));
90   DCHECK(IsAligned(size, allocation_granularity()));
91 
92   OS::Free(reinterpret_cast<void*>(address), size);
93 }
94 
SetPagePermissions(Address address,size_t size,PagePermissions permissions)95 bool VirtualAddressSpace::SetPagePermissions(Address address, size_t size,
96                                              PagePermissions permissions) {
97   DCHECK(IsAligned(address, page_size()));
98   DCHECK(IsAligned(size, page_size()));
99 
100   return OS::SetPermissions(reinterpret_cast<void*>(address), size,
101                             static_cast<OS::MemoryPermission>(permissions));
102 }
103 
AllocateGuardRegion(Address address,size_t size)104 bool VirtualAddressSpace::AllocateGuardRegion(Address address, size_t size) {
105   DCHECK(IsAligned(address, allocation_granularity()));
106   DCHECK(IsAligned(size, allocation_granularity()));
107 
108   void* hint = reinterpret_cast<void*>(address);
109   void* result = OS::Allocate(hint, size, allocation_granularity(),
110                               OS::MemoryPermission::kNoAccess);
111   if (result && result != hint) {
112     OS::Free(result, size);
113   }
114   return result == hint;
115 }
116 
FreeGuardRegion(Address address,size_t size)117 void VirtualAddressSpace::FreeGuardRegion(Address address, size_t size) {
118   DCHECK(IsAligned(address, allocation_granularity()));
119   DCHECK(IsAligned(size, allocation_granularity()));
120 
121   OS::Free(reinterpret_cast<void*>(address), size);
122 }
123 
CanAllocateSubspaces()124 bool VirtualAddressSpace::CanAllocateSubspaces() {
125   return OS::CanReserveAddressSpace();
126 }
127 
AllocateSharedPages(Address hint,size_t size,PagePermissions permissions,PlatformSharedMemoryHandle handle,uint64_t offset)128 Address VirtualAddressSpace::AllocateSharedPages(
129     Address hint, size_t size, PagePermissions permissions,
130     PlatformSharedMemoryHandle handle, uint64_t offset) {
131   DCHECK(IsAligned(hint, allocation_granularity()));
132   DCHECK(IsAligned(size, allocation_granularity()));
133   DCHECK(IsAligned(offset, allocation_granularity()));
134 
135   return reinterpret_cast<Address>(OS::AllocateShared(
136       reinterpret_cast<void*>(hint), size,
137       static_cast<OS::MemoryPermission>(permissions), handle, offset));
138 }
139 
FreeSharedPages(Address address,size_t size)140 void VirtualAddressSpace::FreeSharedPages(Address address, size_t size) {
141   DCHECK(IsAligned(address, allocation_granularity()));
142   DCHECK(IsAligned(size, allocation_granularity()));
143 
144   OS::FreeShared(reinterpret_cast<void*>(address), size);
145 }
146 
AllocateSubspace(Address hint,size_t size,size_t alignment,PagePermissions max_page_permissions)147 std::unique_ptr<v8::VirtualAddressSpace> VirtualAddressSpace::AllocateSubspace(
148     Address hint, size_t size, size_t alignment,
149     PagePermissions max_page_permissions) {
150   DCHECK(IsAligned(alignment, allocation_granularity()));
151   DCHECK(IsAligned(hint, alignment));
152   DCHECK(IsAligned(size, allocation_granularity()));
153 
154   base::Optional<AddressSpaceReservation> reservation =
155       OS::CreateAddressSpaceReservation(
156           reinterpret_cast<void*>(hint), size, alignment,
157           static_cast<OS::MemoryPermission>(max_page_permissions));
158   if (!reservation.has_value())
159     return std::unique_ptr<v8::VirtualAddressSpace>();
160   return std::unique_ptr<v8::VirtualAddressSpace>(
161       new VirtualAddressSubspace(*reservation, this, max_page_permissions));
162 }
163 
DiscardSystemPages(Address address,size_t size)164 bool VirtualAddressSpace::DiscardSystemPages(Address address, size_t size) {
165   DCHECK(IsAligned(address, page_size()));
166   DCHECK(IsAligned(size, page_size()));
167 
168   return OS::DiscardSystemPages(reinterpret_cast<void*>(address), size);
169 }
170 
DecommitPages(Address address,size_t size)171 bool VirtualAddressSpace::DecommitPages(Address address, size_t size) {
172   DCHECK(IsAligned(address, page_size()));
173   DCHECK(IsAligned(size, page_size()));
174 
175   return OS::DecommitPages(reinterpret_cast<void*>(address), size);
176 }
177 
FreeSubspace(VirtualAddressSubspace * subspace)178 void VirtualAddressSpace::FreeSubspace(VirtualAddressSubspace* subspace) {
179   OS::FreeAddressSpaceReservation(subspace->reservation_);
180 }
181 
VirtualAddressSubspace(AddressSpaceReservation reservation,VirtualAddressSpaceBase * parent_space,PagePermissions max_page_permissions)182 VirtualAddressSubspace::VirtualAddressSubspace(
183     AddressSpaceReservation reservation, VirtualAddressSpaceBase* parent_space,
184     PagePermissions max_page_permissions)
185     : VirtualAddressSpaceBase(parent_space->page_size(),
186                               parent_space->allocation_granularity(),
187                               reinterpret_cast<Address>(reservation.base()),
188                               reservation.size(), max_page_permissions),
189       reservation_(reservation),
190       region_allocator_(reinterpret_cast<Address>(reservation.base()),
191                         reservation.size(),
192                         parent_space->allocation_granularity()),
193       parent_space_(parent_space) {
194 #if V8_OS_WIN
195   // On Windows, the address space reservation needs to be split and merged at
196   // the OS level as well.
197   region_allocator_.set_on_split_callback([this](Address start, size_t size) {
198     DCHECK(IsAligned(start, allocation_granularity()));
199     CHECK(reservation_.SplitPlaceholder(reinterpret_cast<void*>(start), size));
200   });
201   region_allocator_.set_on_merge_callback([this](Address start, size_t size) {
202     DCHECK(IsAligned(start, allocation_granularity()));
203     CHECK(reservation_.MergePlaceholders(reinterpret_cast<void*>(start), size));
204   });
205 #endif  // V8_OS_WIN
206 }
207 
~VirtualAddressSubspace()208 VirtualAddressSubspace::~VirtualAddressSubspace() {
209   parent_space_->FreeSubspace(this);
210 }
211 
SetRandomSeed(int64_t seed)212 void VirtualAddressSubspace::SetRandomSeed(int64_t seed) {
213   MutexGuard guard(&mutex_);
214   rng_.SetSeed(seed);
215 }
216 
RandomPageAddress()217 Address VirtualAddressSubspace::RandomPageAddress() {
218   MutexGuard guard(&mutex_);
219   // Note: the random numbers generated here aren't uniformly distributed if the
220   // size isn't a power of two.
221   Address addr = base() + (static_cast<uint64_t>(rng_.NextInt64()) % size());
222   return RoundDown(addr, allocation_granularity());
223 }
224 
AllocatePages(Address hint,size_t size,size_t alignment,PagePermissions permissions)225 Address VirtualAddressSubspace::AllocatePages(Address hint, size_t size,
226                                               size_t alignment,
227                                               PagePermissions permissions) {
228   DCHECK(IsAligned(alignment, allocation_granularity()));
229   DCHECK(IsAligned(hint, alignment));
230   DCHECK(IsAligned(size, allocation_granularity()));
231   DCHECK(IsSubset(permissions, max_page_permissions()));
232 
233   MutexGuard guard(&mutex_);
234 
235   Address address = region_allocator_.AllocateRegion(hint, size, alignment);
236   if (address == RegionAllocator::kAllocationFailure) return kNullAddress;
237 
238   if (!reservation_.Allocate(reinterpret_cast<void*>(address), size,
239                              static_cast<OS::MemoryPermission>(permissions))) {
240     // This most likely means that we ran out of memory.
241     CHECK_EQ(size, region_allocator_.FreeRegion(address));
242     return kNullAddress;
243   }
244 
245   return address;
246 }
247 
FreePages(Address address,size_t size)248 void VirtualAddressSubspace::FreePages(Address address, size_t size) {
249   DCHECK(IsAligned(address, allocation_granularity()));
250   DCHECK(IsAligned(size, allocation_granularity()));
251 
252   MutexGuard guard(&mutex_);
253   // The order here is important: on Windows, the allocation first has to be
254   // freed to a placeholder before the placeholder can be merged (during the
255   // merge_callback) with any surrounding placeholder mappings.
256   CHECK(reservation_.Free(reinterpret_cast<void*>(address), size));
257   CHECK_EQ(size, region_allocator_.FreeRegion(address));
258 }
259 
SetPagePermissions(Address address,size_t size,PagePermissions permissions)260 bool VirtualAddressSubspace::SetPagePermissions(Address address, size_t size,
261                                                 PagePermissions permissions) {
262   DCHECK(IsAligned(address, page_size()));
263   DCHECK(IsAligned(size, page_size()));
264   DCHECK(IsSubset(permissions, max_page_permissions()));
265 
266   return reservation_.SetPermissions(
267       reinterpret_cast<void*>(address), size,
268       static_cast<OS::MemoryPermission>(permissions));
269 }
270 
AllocateGuardRegion(Address address,size_t size)271 bool VirtualAddressSubspace::AllocateGuardRegion(Address address, size_t size) {
272   DCHECK(IsAligned(address, allocation_granularity()));
273   DCHECK(IsAligned(size, allocation_granularity()));
274 
275   MutexGuard guard(&mutex_);
276 
277   // It is guaranteed that reserved address space is inaccessible, so we just
278   // need to mark the region as in-use in the region allocator.
279   return region_allocator_.AllocateRegionAt(address, size);
280 }
281 
FreeGuardRegion(Address address,size_t size)282 void VirtualAddressSubspace::FreeGuardRegion(Address address, size_t size) {
283   DCHECK(IsAligned(address, allocation_granularity()));
284   DCHECK(IsAligned(size, allocation_granularity()));
285 
286   MutexGuard guard(&mutex_);
287   CHECK_EQ(size, region_allocator_.FreeRegion(address));
288 }
289 
AllocateSharedPages(Address hint,size_t size,PagePermissions permissions,PlatformSharedMemoryHandle handle,uint64_t offset)290 Address VirtualAddressSubspace::AllocateSharedPages(
291     Address hint, size_t size, PagePermissions permissions,
292     PlatformSharedMemoryHandle handle, uint64_t offset) {
293   DCHECK(IsAligned(hint, allocation_granularity()));
294   DCHECK(IsAligned(size, allocation_granularity()));
295   DCHECK(IsAligned(offset, allocation_granularity()));
296 
297   MutexGuard guard(&mutex_);
298 
299   Address address =
300       region_allocator_.AllocateRegion(hint, size, allocation_granularity());
301   if (address == RegionAllocator::kAllocationFailure) return kNullAddress;
302 
303   if (!reservation_.AllocateShared(
304           reinterpret_cast<void*>(address), size,
305           static_cast<OS::MemoryPermission>(permissions), handle, offset)) {
306     CHECK_EQ(size, region_allocator_.FreeRegion(address));
307     return kNullAddress;
308   }
309 
310   return address;
311 }
312 
FreeSharedPages(Address address,size_t size)313 void VirtualAddressSubspace::FreeSharedPages(Address address, size_t size) {
314   DCHECK(IsAligned(address, allocation_granularity()));
315   DCHECK(IsAligned(size, allocation_granularity()));
316 
317   MutexGuard guard(&mutex_);
318   // The order here is important: on Windows, the allocation first has to be
319   // freed to a placeholder before the placeholder can be merged (during the
320   // merge_callback) with any surrounding placeholder mappings.
321   CHECK(reservation_.FreeShared(reinterpret_cast<void*>(address), size));
322   CHECK_EQ(size, region_allocator_.FreeRegion(address));
323 }
324 
325 std::unique_ptr<v8::VirtualAddressSpace>
AllocateSubspace(Address hint,size_t size,size_t alignment,PagePermissions max_page_permissions)326 VirtualAddressSubspace::AllocateSubspace(Address hint, size_t size,
327                                          size_t alignment,
328                                          PagePermissions max_page_permissions) {
329   DCHECK(IsAligned(alignment, allocation_granularity()));
330   DCHECK(IsAligned(hint, alignment));
331   DCHECK(IsAligned(size, allocation_granularity()));
332   DCHECK(IsSubset(max_page_permissions, this->max_page_permissions()));
333 
334   MutexGuard guard(&mutex_);
335 
336   Address address = region_allocator_.AllocateRegion(hint, size, alignment);
337   if (address == RegionAllocator::kAllocationFailure) {
338     return std::unique_ptr<v8::VirtualAddressSpace>();
339   }
340 
341   base::Optional<AddressSpaceReservation> reservation =
342       reservation_.CreateSubReservation(
343           reinterpret_cast<void*>(address), size,
344           static_cast<OS::MemoryPermission>(max_page_permissions));
345   if (!reservation.has_value()) {
346     CHECK_EQ(size, region_allocator_.FreeRegion(address));
347     return nullptr;
348   }
349   return std::unique_ptr<v8::VirtualAddressSpace>(
350       new VirtualAddressSubspace(*reservation, this, max_page_permissions));
351 }
352 
DiscardSystemPages(Address address,size_t size)353 bool VirtualAddressSubspace::DiscardSystemPages(Address address, size_t size) {
354   DCHECK(IsAligned(address, page_size()));
355   DCHECK(IsAligned(size, page_size()));
356 
357   return reservation_.DiscardSystemPages(reinterpret_cast<void*>(address),
358                                          size);
359 }
360 
DecommitPages(Address address,size_t size)361 bool VirtualAddressSubspace::DecommitPages(Address address, size_t size) {
362   DCHECK(IsAligned(address, page_size()));
363   DCHECK(IsAligned(size, page_size()));
364 
365   return reservation_.DecommitPages(reinterpret_cast<void*>(address), size);
366 }
367 
FreeSubspace(VirtualAddressSubspace * subspace)368 void VirtualAddressSubspace::FreeSubspace(VirtualAddressSubspace* subspace) {
369   MutexGuard guard(&mutex_);
370 
371   AddressSpaceReservation reservation = subspace->reservation_;
372   Address base = reinterpret_cast<Address>(reservation.base());
373   CHECK_EQ(reservation.size(), region_allocator_.FreeRegion(base));
374   CHECK(reservation_.FreeSubReservation(reservation));
375 }
376 
377 }  // namespace base
378 }  // namespace v8
379