| File: | out/../deps/v8/src/utils/allocation.cc |
| Warning: | line 311, column 36 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | // Copyright 2012 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/utils/allocation.h" | |||
| 6 | ||||
| 7 | #include <stdlib.h> // For free, malloc. | |||
| 8 | ||||
| 9 | #include "src/base/bits.h" | |||
| 10 | #include "src/base/bounded-page-allocator.h" | |||
| 11 | #include "src/base/lazy-instance.h" | |||
| 12 | #include "src/base/logging.h" | |||
| 13 | #include "src/base/page-allocator.h" | |||
| 14 | #include "src/base/platform/platform.h" | |||
| 15 | #include "src/base/platform/wrappers.h" | |||
| 16 | #include "src/base/sanitizer/lsan-page-allocator.h" | |||
| 17 | #include "src/base/sanitizer/lsan-virtual-address-space.h" | |||
| 18 | #include "src/base/vector.h" | |||
| 19 | #include "src/base/virtual-address-space.h" | |||
| 20 | #include "src/flags/flags.h" | |||
| 21 | #include "src/init/v8.h" | |||
| 22 | #include "src/sandbox/sandbox.h" | |||
| 23 | #include "src/utils/memcopy.h" | |||
| 24 | ||||
| 25 | #if V8_LIBC_BIONIC | |||
| 26 | #include <malloc.h> | |||
| 27 | #endif | |||
| 28 | ||||
| 29 | namespace v8 { | |||
| 30 | namespace internal { | |||
| 31 | ||||
| 32 | namespace { | |||
| 33 | ||||
| 34 | void* AlignedAllocInternal(size_t size, size_t alignment) { | |||
| 35 | void* ptr; | |||
| 36 | #if V8_OS_WIN | |||
| 37 | ptr = _aligned_malloc(size, alignment); | |||
| 38 | #elif V8_LIBC_BIONIC | |||
| 39 | // posix_memalign is not exposed in some Android versions, so we fall back to | |||
| 40 | // memalign. See http://code.google.com/p/android/issues/detail?id=35391. | |||
| 41 | ptr = memalign(alignment, size); | |||
| 42 | #elif V8_OS_STARBOARD | |||
| 43 | ptr = SbMemoryAllocateAligned(alignment, size); | |||
| 44 | #else | |||
| 45 | if (posix_memalign(&ptr, alignment, size)) ptr = nullptr; | |||
| 46 | #endif | |||
| 47 | return ptr; | |||
| 48 | } | |||
| 49 | ||||
| 50 | class PageAllocatorInitializer { | |||
| 51 | public: | |||
| 52 | PageAllocatorInitializer() { | |||
| 53 | page_allocator_ = V8::GetCurrentPlatform()->GetPageAllocator(); | |||
| 54 | if (page_allocator_ == nullptr) { | |||
| 55 | static base::LeakyObject<base::PageAllocator> default_page_allocator; | |||
| 56 | page_allocator_ = default_page_allocator.get(); | |||
| 57 | } | |||
| 58 | #if defined(LEAK_SANITIZER) | |||
| 59 | static base::LeakyObject<base::LsanPageAllocator> lsan_allocator( | |||
| 60 | page_allocator_); | |||
| 61 | page_allocator_ = lsan_allocator.get(); | |||
| 62 | #endif | |||
| 63 | } | |||
| 64 | ||||
| 65 | PageAllocator* page_allocator() const { return page_allocator_; } | |||
| 66 | ||||
| 67 | void SetPageAllocatorForTesting(PageAllocator* allocator) { | |||
| 68 | page_allocator_ = allocator; | |||
| 69 | } | |||
| 70 | ||||
| 71 | private: | |||
| 72 | PageAllocator* page_allocator_; | |||
| 73 | }; | |||
| 74 | ||||
| 75 | DEFINE_LAZY_LEAKY_OBJECT_GETTER(PageAllocatorInitializer,PageAllocatorInitializer* GetPageAllocatorInitializer() { static ::v8::base::LeakyObject<PageAllocatorInitializer> object {}; return object.get(); } | |||
| 76 | GetPageAllocatorInitializer)PageAllocatorInitializer* GetPageAllocatorInitializer() { static ::v8::base::LeakyObject<PageAllocatorInitializer> object {}; return object.get(); } | |||
| 77 | ||||
| 78 | // We will attempt allocation this many times. After each failure, we call | |||
| 79 | // OnCriticalMemoryPressure to try to free some memory. | |||
| 80 | const int kAllocationTries = 2; | |||
| 81 | ||||
| 82 | } // namespace | |||
| 83 | ||||
| 84 | v8::PageAllocator* GetPlatformPageAllocator() { | |||
| 85 | DCHECK_NOT_NULL(GetPageAllocatorInitializer()->page_allocator())((void) 0); | |||
| 86 | return GetPageAllocatorInitializer()->page_allocator(); | |||
| 87 | } | |||
| 88 | ||||
| 89 | v8::VirtualAddressSpace* GetPlatformVirtualAddressSpace() { | |||
| 90 | #if defined(LEAK_SANITIZER) | |||
| 91 | static base::LeakyObject<base::LsanVirtualAddressSpace> vas( | |||
| 92 | std::make_unique<base::VirtualAddressSpace>()); | |||
| 93 | #else | |||
| 94 | static base::LeakyObject<base::VirtualAddressSpace> vas; | |||
| 95 | #endif | |||
| 96 | return vas.get(); | |||
| 97 | } | |||
| 98 | ||||
| 99 | #ifdef V8_SANDBOX | |||
| 100 | v8::PageAllocator* GetSandboxPageAllocator() { | |||
| 101 | // TODO(chromium:1218005) remove this code once the cage is no longer | |||
| 102 | // optional. | |||
| 103 | if (GetProcessWideSandbox()->is_disabled()) { | |||
| 104 | return GetPlatformPageAllocator(); | |||
| 105 | } else { | |||
| 106 | CHECK(GetProcessWideSandbox()->is_initialized())do { if ((__builtin_expect(!!(!(GetProcessWideSandbox()->is_initialized ())), 0))) { V8_Fatal("Check failed: %s.", "GetProcessWideSandbox()->is_initialized()" ); } } while (false); | |||
| 107 | return GetProcessWideSandbox()->page_allocator(); | |||
| 108 | } | |||
| 109 | } | |||
| 110 | #endif | |||
| 111 | ||||
| 112 | v8::PageAllocator* SetPlatformPageAllocatorForTesting( | |||
| 113 | v8::PageAllocator* new_page_allocator) { | |||
| 114 | v8::PageAllocator* old_page_allocator = GetPlatformPageAllocator(); | |||
| 115 | GetPageAllocatorInitializer()->SetPageAllocatorForTesting(new_page_allocator); | |||
| 116 | return old_page_allocator; | |||
| 117 | } | |||
| 118 | ||||
| 119 | void* Malloced::operator new(size_t size) { | |||
| 120 | void* result = AllocWithRetry(size); | |||
| 121 | if (result == nullptr) { | |||
| 122 | V8::FatalProcessOutOfMemory(nullptr, "Malloced operator new"); | |||
| 123 | } | |||
| 124 | return result; | |||
| 125 | } | |||
| 126 | ||||
| 127 | void Malloced::operator delete(void* p) { base::Free(p); } | |||
| 128 | ||||
| 129 | char* StrDup(const char* str) { | |||
| 130 | size_t length = strlen(str); | |||
| 131 | char* result = NewArray<char>(length + 1); | |||
| 132 | MemCopy(result, str, length); | |||
| 133 | result[length] = '\0'; | |||
| 134 | return result; | |||
| 135 | } | |||
| 136 | ||||
| 137 | char* StrNDup(const char* str, size_t n) { | |||
| 138 | size_t length = strlen(str); | |||
| 139 | if (n < length) length = n; | |||
| 140 | char* result = NewArray<char>(length + 1); | |||
| 141 | MemCopy(result, str, length); | |||
| 142 | result[length] = '\0'; | |||
| 143 | return result; | |||
| 144 | } | |||
| 145 | ||||
| 146 | void* AllocWithRetry(size_t size, MallocFn malloc_fn) { | |||
| 147 | void* result = nullptr; | |||
| 148 | for (int i = 0; i < kAllocationTries; ++i) { | |||
| 149 | result = malloc_fn(size); | |||
| 150 | if (result != nullptr) break; | |||
| 151 | if (!OnCriticalMemoryPressure(size)) break; | |||
| 152 | } | |||
| 153 | return result; | |||
| 154 | } | |||
| 155 | ||||
| 156 | void* AlignedAlloc(size_t size, size_t alignment) { | |||
| 157 | DCHECK_LE(alignof(void*), alignment)((void) 0); | |||
| 158 | DCHECK(base::bits::IsPowerOfTwo(alignment))((void) 0); | |||
| 159 | void* result = nullptr; | |||
| 160 | for (int i = 0; i < kAllocationTries; ++i) { | |||
| 161 | result = AlignedAllocInternal(size, alignment); | |||
| 162 | if (result != nullptr) break; | |||
| 163 | if (!OnCriticalMemoryPressure(size + alignment)) break; | |||
| 164 | } | |||
| 165 | if (result == nullptr) { | |||
| 166 | V8::FatalProcessOutOfMemory(nullptr, "AlignedAlloc"); | |||
| 167 | } | |||
| 168 | return result; | |||
| 169 | } | |||
| 170 | ||||
| 171 | void AlignedFree(void* ptr) { | |||
| 172 | #if V8_OS_WIN | |||
| 173 | _aligned_free(ptr); | |||
| 174 | #elif V8_LIBC_BIONIC | |||
| 175 | // Using free is not correct in general, but for V8_LIBC_BIONIC it is. | |||
| 176 | base::Free(ptr); | |||
| 177 | #elif V8_OS_STARBOARD | |||
| 178 | SbMemoryFreeAligned(ptr); | |||
| 179 | #else | |||
| 180 | base::Free(ptr); | |||
| 181 | #endif | |||
| 182 | } | |||
| 183 | ||||
| 184 | size_t AllocatePageSize() { | |||
| 185 | return GetPlatformPageAllocator()->AllocatePageSize(); | |||
| 186 | } | |||
| 187 | ||||
| 188 | size_t CommitPageSize() { return GetPlatformPageAllocator()->CommitPageSize(); } | |||
| 189 | ||||
| 190 | void SetRandomMmapSeed(int64_t seed) { | |||
| 191 | GetPlatformPageAllocator()->SetRandomMmapSeed(seed); | |||
| 192 | } | |||
| 193 | ||||
| 194 | void* GetRandomMmapAddr() { | |||
| 195 | return GetPlatformPageAllocator()->GetRandomMmapAddr(); | |||
| 196 | } | |||
| 197 | ||||
| 198 | void* AllocatePages(v8::PageAllocator* page_allocator, void* hint, size_t size, | |||
| 199 | size_t alignment, PageAllocator::Permission access) { | |||
| 200 | DCHECK_NOT_NULL(page_allocator)((void) 0); | |||
| 201 | DCHECK_EQ(hint, AlignedAddress(hint, alignment))((void) 0); | |||
| 202 | DCHECK(IsAligned(size, page_allocator->AllocatePageSize()))((void) 0); | |||
| 203 | if (FLAG_randomize_all_allocations) { | |||
| 204 | hint = AlignedAddress(page_allocator->GetRandomMmapAddr(), alignment); | |||
| 205 | } | |||
| 206 | void* result = nullptr; | |||
| 207 | for (int i = 0; i < kAllocationTries; ++i) { | |||
| 208 | result = page_allocator->AllocatePages(hint, size, alignment, access); | |||
| 209 | if (result != nullptr) break; | |||
| 210 | size_t request_size = size + alignment - page_allocator->AllocatePageSize(); | |||
| 211 | if (!OnCriticalMemoryPressure(request_size)) break; | |||
| 212 | } | |||
| 213 | return result; | |||
| 214 | } | |||
| 215 | ||||
| 216 | void FreePages(v8::PageAllocator* page_allocator, void* address, | |||
| 217 | const size_t size) { | |||
| 218 | DCHECK_NOT_NULL(page_allocator)((void) 0); | |||
| 219 | DCHECK(IsAligned(size, page_allocator->AllocatePageSize()))((void) 0); | |||
| 220 | CHECK(page_allocator->FreePages(address, size))do { if ((__builtin_expect(!!(!(page_allocator->FreePages( address, size))), 0))) { V8_Fatal("Check failed: %s.", "page_allocator->FreePages(address, size)" ); } } while (false); | |||
| 221 | } | |||
| 222 | ||||
| 223 | void ReleasePages(v8::PageAllocator* page_allocator, void* address, size_t size, | |||
| 224 | size_t new_size) { | |||
| 225 | DCHECK_NOT_NULL(page_allocator)((void) 0); | |||
| 226 | DCHECK_LT(new_size, size)((void) 0); | |||
| 227 | DCHECK(IsAligned(new_size, page_allocator->CommitPageSize()))((void) 0); | |||
| 228 | CHECK(page_allocator->ReleasePages(address, size, new_size))do { if ((__builtin_expect(!!(!(page_allocator->ReleasePages (address, size, new_size))), 0))) { V8_Fatal("Check failed: %s." , "page_allocator->ReleasePages(address, size, new_size)") ; } } while (false); | |||
| 229 | } | |||
| 230 | ||||
| 231 | bool SetPermissions(v8::PageAllocator* page_allocator, void* address, | |||
| 232 | size_t size, PageAllocator::Permission access) { | |||
| 233 | DCHECK_NOT_NULL(page_allocator)((void) 0); | |||
| 234 | return page_allocator->SetPermissions(address, size, access); | |||
| 235 | } | |||
| 236 | ||||
| 237 | bool OnCriticalMemoryPressure(size_t length) { | |||
| 238 | // TODO(bbudge) Rework retry logic once embedders implement the more | |||
| 239 | // informative overload. | |||
| 240 | if (!V8::GetCurrentPlatform()->OnCriticalMemoryPressure(length)) { | |||
| 241 | V8::GetCurrentPlatform()->OnCriticalMemoryPressure(); | |||
| 242 | } | |||
| 243 | return true; | |||
| 244 | } | |||
| 245 | ||||
| 246 | VirtualMemory::VirtualMemory() = default; | |||
| 247 | ||||
| 248 | VirtualMemory::VirtualMemory(v8::PageAllocator* page_allocator, size_t size, | |||
| 249 | void* hint, size_t alignment, JitPermission jit) | |||
| 250 | : page_allocator_(page_allocator) { | |||
| 251 | DCHECK_NOT_NULL(page_allocator)((void) 0); | |||
| 252 | DCHECK(IsAligned(size, page_allocator_->CommitPageSize()))((void) 0); | |||
| 253 | size_t page_size = page_allocator_->AllocatePageSize(); | |||
| 254 | alignment = RoundUp(alignment, page_size); | |||
| 255 | PageAllocator::Permission permissions = | |||
| 256 | jit == kMapAsJittable ? PageAllocator::kNoAccessWillJitLater | |||
| 257 | : PageAllocator::kNoAccess; | |||
| 258 | Address address = reinterpret_cast<Address>(AllocatePages( | |||
| 259 | page_allocator_, hint, RoundUp(size, page_size), alignment, permissions)); | |||
| 260 | if (address != kNullAddress) { | |||
| 261 | DCHECK(IsAligned(address, alignment))((void) 0); | |||
| 262 | region_ = base::AddressRegion(address, size); | |||
| 263 | } | |||
| 264 | } | |||
| 265 | ||||
| 266 | VirtualMemory::~VirtualMemory() { | |||
| 267 | if (IsReserved()) { | |||
| 268 | Free(); | |||
| 269 | } | |||
| 270 | } | |||
| 271 | ||||
| 272 | void VirtualMemory::Reset() { | |||
| 273 | page_allocator_ = nullptr; | |||
| 274 | region_ = base::AddressRegion(); | |||
| 275 | } | |||
| 276 | ||||
| 277 | bool VirtualMemory::SetPermissions(Address address, size_t size, | |||
| 278 | PageAllocator::Permission access) { | |||
| 279 | CHECK(InVM(address, size))do { if ((__builtin_expect(!!(!(InVM(address, size))), 0))) { V8_Fatal("Check failed: %s.", "InVM(address, size)"); } } while (false); | |||
| 280 | bool result = | |||
| 281 | v8::internal::SetPermissions(page_allocator_, address, size, access); | |||
| 282 | DCHECK(result)((void) 0); | |||
| 283 | return result; | |||
| 284 | } | |||
| 285 | ||||
| 286 | size_t VirtualMemory::Release(Address free_start) { | |||
| 287 | DCHECK(IsReserved())((void) 0); | |||
| 288 | DCHECK(IsAligned(free_start, page_allocator_->CommitPageSize()))((void) 0); | |||
| 289 | // Notice: Order is important here. The VirtualMemory object might live | |||
| 290 | // inside the allocated region. | |||
| 291 | ||||
| 292 | const size_t old_size = region_.size(); | |||
| 293 | const size_t free_size = old_size - (free_start - region_.begin()); | |||
| 294 | CHECK(InVM(free_start, free_size))do { if ((__builtin_expect(!!(!(InVM(free_start, free_size))) , 0))) { V8_Fatal("Check failed: %s.", "InVM(free_start, free_size)" ); } } while (false); | |||
| 295 | region_.set_size(old_size - free_size); | |||
| 296 | ReleasePages(page_allocator_, reinterpret_cast<void*>(region_.begin()), | |||
| 297 | old_size, region_.size()); | |||
| 298 | return free_size; | |||
| 299 | } | |||
| 300 | ||||
| 301 | void VirtualMemory::Free() { | |||
| 302 | DCHECK(IsReserved())((void) 0); | |||
| 303 | // Notice: Order is important here. The VirtualMemory object might live | |||
| 304 | // inside the allocated region. | |||
| 305 | v8::PageAllocator* page_allocator = page_allocator_; | |||
| 306 | base::AddressRegion region = region_; | |||
| 307 | Reset(); | |||
| 308 | // FreePages expects size to be aligned to allocation granularity however | |||
| 309 | // ReleasePages may leave size at only commit granularity. Align it here. | |||
| 310 | FreePages(page_allocator, reinterpret_cast<void*>(region.begin()), | |||
| 311 | RoundUp(region.size(), page_allocator->AllocatePageSize())); | |||
| ||||
| 312 | } | |||
| 313 | ||||
| 314 | void VirtualMemory::FreeReadOnly() { | |||
| 315 | DCHECK(IsReserved())((void) 0); | |||
| 316 | // The only difference to Free is that it doesn't call Reset which would write | |||
| 317 | // to the VirtualMemory object. | |||
| 318 | v8::PageAllocator* page_allocator = page_allocator_; | |||
| 319 | base::AddressRegion region = region_; | |||
| 320 | ||||
| 321 | // FreePages expects size to be aligned to allocation granularity however | |||
| 322 | // ReleasePages may leave size at only commit granularity. Align it here. | |||
| 323 | FreePages(page_allocator, reinterpret_cast<void*>(region.begin()), | |||
| 324 | RoundUp(region.size(), page_allocator->AllocatePageSize())); | |||
| 325 | } | |||
| 326 | ||||
| 327 | VirtualMemoryCage::VirtualMemoryCage() = default; | |||
| 328 | ||||
| 329 | VirtualMemoryCage::~VirtualMemoryCage() { Free(); } | |||
| 330 | ||||
| 331 | VirtualMemoryCage::VirtualMemoryCage(VirtualMemoryCage&& other) V8_NOEXCEPTnoexcept { | |||
| 332 | *this = std::move(other); | |||
| 333 | } | |||
| 334 | ||||
| 335 | VirtualMemoryCage& VirtualMemoryCage::operator=(VirtualMemoryCage&& other) | |||
| 336 | V8_NOEXCEPTnoexcept { | |||
| 337 | page_allocator_ = std::move(other.page_allocator_); | |||
| 338 | reservation_ = std::move(other.reservation_); | |||
| 339 | return *this; | |||
| 340 | } | |||
| 341 | ||||
| 342 | namespace { | |||
| 343 | inline Address VirtualMemoryCageStart( | |||
| 344 | Address reservation_start, | |||
| 345 | const VirtualMemoryCage::ReservationParams& params) { | |||
| 346 | return RoundUp(reservation_start + params.base_bias_size, | |||
| 347 | params.base_alignment) - | |||
| 348 | params.base_bias_size; | |||
| 349 | } | |||
| 350 | } // namespace | |||
| 351 | ||||
| 352 | bool VirtualMemoryCage::InitReservation( | |||
| 353 | const ReservationParams& params, base::AddressRegion existing_reservation) { | |||
| 354 | DCHECK(!reservation_.IsReserved())((void) 0); | |||
| 355 | ||||
| 356 | const size_t allocate_page_size = params.page_allocator->AllocatePageSize(); | |||
| 357 | CHECK(IsAligned(params.reservation_size, allocate_page_size))do { if ((__builtin_expect(!!(!(IsAligned(params.reservation_size , allocate_page_size))), 0))) { V8_Fatal("Check failed: %s.", "IsAligned(params.reservation_size, allocate_page_size)"); } } while (false); | |||
| ||||
| 358 | CHECK(params.base_alignment == ReservationParams::kAnyBaseAlignment ||do { if ((__builtin_expect(!!(!(params.base_alignment == ReservationParams ::kAnyBaseAlignment || (IsAligned(params.base_alignment, allocate_page_size ) && IsAligned(params.base_bias_size, allocate_page_size )))), 0))) { V8_Fatal("Check failed: %s.", "params.base_alignment == ReservationParams::kAnyBaseAlignment || (IsAligned(params.base_alignment, allocate_page_size) && IsAligned(params.base_bias_size, allocate_page_size))" ); } } while (false) | |||
| 359 | (IsAligned(params.base_alignment, allocate_page_size) &&do { if ((__builtin_expect(!!(!(params.base_alignment == ReservationParams ::kAnyBaseAlignment || (IsAligned(params.base_alignment, allocate_page_size ) && IsAligned(params.base_bias_size, allocate_page_size )))), 0))) { V8_Fatal("Check failed: %s.", "params.base_alignment == ReservationParams::kAnyBaseAlignment || (IsAligned(params.base_alignment, allocate_page_size) && IsAligned(params.base_bias_size, allocate_page_size))" ); } } while (false) | |||
| 360 | IsAligned(params.base_bias_size, allocate_page_size)))do { if ((__builtin_expect(!!(!(params.base_alignment == ReservationParams ::kAnyBaseAlignment || (IsAligned(params.base_alignment, allocate_page_size ) && IsAligned(params.base_bias_size, allocate_page_size )))), 0))) { V8_Fatal("Check failed: %s.", "params.base_alignment == ReservationParams::kAnyBaseAlignment || (IsAligned(params.base_alignment, allocate_page_size) && IsAligned(params.base_bias_size, allocate_page_size))" ); } } while (false); | |||
| 361 | CHECK_LE(params.base_bias_size, params.reservation_size)do { bool _cmp = ::v8::base::CmpLEImpl< typename ::v8::base ::pass_value_or_ref<decltype(params.base_bias_size)>::type , typename ::v8::base::pass_value_or_ref<decltype(params.reservation_size )>::type>((params.base_bias_size), (params.reservation_size )); do { if ((__builtin_expect(!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s." , "params.base_bias_size" " " "<=" " " "params.reservation_size" ); } } while (false); } while (false); | |||
| 362 | ||||
| 363 | if (!existing_reservation.is_empty()) { | |||
| 364 | CHECK_EQ(existing_reservation.size(), params.reservation_size)do { bool _cmp = ::v8::base::CmpEQImpl< typename ::v8::base ::pass_value_or_ref<decltype(existing_reservation.size())> ::type, typename ::v8::base::pass_value_or_ref<decltype(params .reservation_size)>::type>((existing_reservation.size() ), (params.reservation_size)); do { if ((__builtin_expect(!!( !(_cmp)), 0))) { V8_Fatal("Check failed: %s.", "existing_reservation.size()" " " "==" " " "params.reservation_size"); } } while (false); } while (false); | |||
| 365 | CHECK(params.base_alignment == ReservationParams::kAnyBaseAlignment ||do { if ((__builtin_expect(!!(!(params.base_alignment == ReservationParams ::kAnyBaseAlignment || IsAligned(existing_reservation.begin() , params.base_alignment))), 0))) { V8_Fatal("Check failed: %s." , "params.base_alignment == ReservationParams::kAnyBaseAlignment || IsAligned(existing_reservation.begin(), params.base_alignment)" ); } } while (false) | |||
| 366 | IsAligned(existing_reservation.begin(), params.base_alignment))do { if ((__builtin_expect(!!(!(params.base_alignment == ReservationParams ::kAnyBaseAlignment || IsAligned(existing_reservation.begin() , params.base_alignment))), 0))) { V8_Fatal("Check failed: %s." , "params.base_alignment == ReservationParams::kAnyBaseAlignment || IsAligned(existing_reservation.begin(), params.base_alignment)" ); } } while (false); | |||
| 367 | reservation_ = | |||
| 368 | VirtualMemory(params.page_allocator, existing_reservation.begin(), | |||
| 369 | existing_reservation.size()); | |||
| 370 | base_ = reservation_.address() + params.base_bias_size; | |||
| 371 | } else if (params.base_alignment == ReservationParams::kAnyBaseAlignment || | |||
| 372 | params.base_bias_size == 0) { | |||
| 373 | // When the base doesn't need to be aligned or when the requested | |||
| 374 | // base_bias_size is zero, the virtual memory reservation fails only | |||
| 375 | // due to OOM. | |||
| 376 | Address hint = | |||
| 377 | RoundDown(params.requested_start_hint, | |||
| 378 | RoundUp(params.base_alignment, allocate_page_size)); | |||
| 379 | VirtualMemory reservation(params.page_allocator, params.reservation_size, | |||
| 380 | reinterpret_cast<void*>(hint), | |||
| 381 | params.base_alignment); | |||
| 382 | if (!reservation.IsReserved()) return false; | |||
| 383 | ||||
| 384 | reservation_ = std::move(reservation); | |||
| 385 | base_ = reservation_.address() + params.base_bias_size; | |||
| 386 | CHECK_EQ(reservation_.size(), params.reservation_size)do { bool _cmp = ::v8::base::CmpEQImpl< typename ::v8::base ::pass_value_or_ref<decltype(reservation_.size())>::type , typename ::v8::base::pass_value_or_ref<decltype(params.reservation_size )>::type>((reservation_.size()), (params.reservation_size )); do { if ((__builtin_expect(!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s." , "reservation_.size()" " " "==" " " "params.reservation_size" ); } } while (false); } while (false); | |||
| 387 | } else { | |||
| 388 | // Otherwise, we need to try harder by first overreserving | |||
| 389 | // in hopes of finding a correctly aligned address within the larger | |||
| 390 | // reservation. | |||
| 391 | Address hint = | |||
| 392 | RoundDown(params.requested_start_hint, | |||
| 393 | RoundUp(params.base_alignment, allocate_page_size)) - | |||
| 394 | RoundUp(params.base_bias_size, allocate_page_size); | |||
| 395 | const int kMaxAttempts = 4; | |||
| 396 | for (int attempt = 0; attempt < kMaxAttempts; ++attempt) { | |||
| 397 | // Reserve a region of twice the size so that there is an aligned address | |||
| 398 | // within it that's usable as the cage base. | |||
| 399 | VirtualMemory padded_reservation(params.page_allocator, | |||
| 400 | params.reservation_size * 2, | |||
| 401 | reinterpret_cast<void*>(hint)); | |||
| 402 | if (!padded_reservation.IsReserved()) return false; | |||
| 403 | ||||
| 404 | // Find properly aligned sub-region inside the reservation. | |||
| 405 | Address address = | |||
| 406 | VirtualMemoryCageStart(padded_reservation.address(), params); | |||
| 407 | CHECK(padded_reservation.InVM(address, params.reservation_size))do { if ((__builtin_expect(!!(!(padded_reservation.InVM(address , params.reservation_size))), 0))) { V8_Fatal("Check failed: %s." , "padded_reservation.InVM(address, params.reservation_size)" ); } } while (false); | |||
| 408 | ||||
| 409 | #if defined(V8_OS_FUCHSIA) | |||
| 410 | // Fuchsia does not respect given hints so as a workaround we will use | |||
| 411 | // overreserved address space region instead of trying to re-reserve | |||
| 412 | // a subregion. | |||
| 413 | bool overreserve = true; | |||
| 414 | #else | |||
| 415 | // For the last attempt use the overreserved region to avoid an OOM crash. | |||
| 416 | // This case can happen if there are many isolates being created in | |||
| 417 | // parallel that race for reserving the regions. | |||
| 418 | bool overreserve = (attempt == kMaxAttempts - 1); | |||
| 419 | #endif | |||
| 420 | ||||
| 421 | if (overreserve) { | |||
| 422 | if (padded_reservation.InVM(address, params.reservation_size)) { | |||
| 423 | reservation_ = std::move(padded_reservation); | |||
| 424 | base_ = address + params.base_bias_size; | |||
| 425 | break; | |||
| 426 | } | |||
| 427 | } else { | |||
| 428 | // Now free the padded reservation and immediately try to reserve an | |||
| 429 | // exact region at aligned address. We have to do this dancing because | |||
| 430 | // the reservation address requirement is more complex than just a | |||
| 431 | // certain alignment and not all operating systems support freeing parts | |||
| 432 | // of reserved address space regions. | |||
| 433 | padded_reservation.Free(); | |||
| 434 | ||||
| 435 | VirtualMemory reservation(params.page_allocator, | |||
| 436 | params.reservation_size, | |||
| 437 | reinterpret_cast<void*>(address)); | |||
| 438 | if (!reservation.IsReserved()) return false; | |||
| 439 | ||||
| 440 | // The reservation could still be somewhere else but we can accept it | |||
| 441 | // if it has the required alignment. | |||
| 442 | Address start_address = | |||
| 443 | VirtualMemoryCageStart(reservation.address(), params); | |||
| 444 | if (reservation.address() == start_address) { | |||
| 445 | reservation_ = std::move(reservation); | |||
| 446 | base_ = start_address + params.base_bias_size; | |||
| 447 | CHECK_EQ(reservation_.size(), params.reservation_size)do { bool _cmp = ::v8::base::CmpEQImpl< typename ::v8::base ::pass_value_or_ref<decltype(reservation_.size())>::type , typename ::v8::base::pass_value_or_ref<decltype(params.reservation_size )>::type>((reservation_.size()), (params.reservation_size )); do { if ((__builtin_expect(!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s." , "reservation_.size()" " " "==" " " "params.reservation_size" ); } } while (false); } while (false); | |||
| 448 | break; | |||
| 449 | } | |||
| 450 | } | |||
| 451 | } | |||
| 452 | } | |||
| 453 | CHECK_NE(base_, kNullAddress)do { bool _cmp = ::v8::base::CmpNEImpl< typename ::v8::base ::pass_value_or_ref<decltype(base_)>::type, typename :: v8::base::pass_value_or_ref<decltype(kNullAddress)>::type >((base_), (kNullAddress)); do { if ((__builtin_expect(!!( !(_cmp)), 0))) { V8_Fatal("Check failed: %s.", "base_" " " "!=" " " "kNullAddress"); } } while (false); } while (false); | |||
| 454 | CHECK(IsAligned(base_, params.base_alignment))do { if ((__builtin_expect(!!(!(IsAligned(base_, params.base_alignment ))), 0))) { V8_Fatal("Check failed: %s.", "IsAligned(base_, params.base_alignment)" ); } } while (false); | |||
| 455 | ||||
| 456 | const Address allocatable_base = RoundUp(base_, params.page_size); | |||
| 457 | const size_t allocatable_size = | |||
| 458 | RoundDown(params.reservation_size - (allocatable_base - base_) - | |||
| 459 | params.base_bias_size, | |||
| 460 | params.page_size); | |||
| 461 | size_ = allocatable_base + allocatable_size - base_; | |||
| 462 | page_allocator_ = std::make_unique<base::BoundedPageAllocator>( | |||
| 463 | params.page_allocator, allocatable_base, allocatable_size, | |||
| 464 | params.page_size, | |||
| 465 | base::PageInitializationMode::kAllocatedPagesCanBeUninitialized); | |||
| 466 | return true; | |||
| 467 | } | |||
| 468 | ||||
| 469 | void VirtualMemoryCage::Free() { | |||
| 470 | if (IsReserved()) { | |||
| 471 | base_ = kNullAddress; | |||
| 472 | size_ = 0; | |||
| 473 | page_allocator_.reset(); | |||
| 474 | reservation_.Free(); | |||
| 475 | } | |||
| 476 | } | |||
| 477 | ||||
| 478 | } // namespace internal | |||
| 479 | } // namespace v8 |
| 1 | // Copyright 2012 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 | #ifndef V8_UTILS_ALLOCATION_H_ |
| 6 | #define V8_UTILS_ALLOCATION_H_ |
| 7 | |
| 8 | #include "include/v8-platform.h" |
| 9 | #include "src/base/address-region.h" |
| 10 | #include "src/base/compiler-specific.h" |
| 11 | #include "src/base/platform/platform.h" |
| 12 | #include "src/common/globals.h" |
| 13 | #include "src/init/v8.h" |
| 14 | |
| 15 | namespace v8 { |
| 16 | |
| 17 | namespace base { |
| 18 | class BoundedPageAllocator; |
| 19 | } // namespace base |
| 20 | |
| 21 | namespace internal { |
| 22 | |
| 23 | class Isolate; |
| 24 | |
| 25 | // This file defines memory allocation functions. If a first attempt at an |
| 26 | // allocation fails, these functions call back into the embedder, then attempt |
| 27 | // the allocation a second time. The embedder callback must not reenter V8. |
| 28 | |
| 29 | // Called when allocation routines fail to allocate, even with a possible retry. |
| 30 | // This function should not return, but should terminate the current processing. |
| 31 | [[noreturn]] V8_EXPORT_PRIVATE void FatalProcessOutOfMemory( |
| 32 | Isolate* isolate, const char* message); |
| 33 | |
| 34 | // Superclass for classes managed with new & delete. |
| 35 | class V8_EXPORT_PRIVATE Malloced { |
| 36 | public: |
| 37 | static void* operator new(size_t size); |
| 38 | static void operator delete(void* p); |
| 39 | }; |
| 40 | |
| 41 | template <typename T> |
| 42 | T* NewArray(size_t size) { |
| 43 | T* result = new (std::nothrow) T[size]; |
| 44 | if (result == nullptr) { |
| 45 | V8::GetCurrentPlatform()->OnCriticalMemoryPressure(); |
| 46 | result = new (std::nothrow) T[size]; |
| 47 | if (result == nullptr) FatalProcessOutOfMemory(nullptr, "NewArray"); |
| 48 | } |
| 49 | return result; |
| 50 | } |
| 51 | |
| 52 | template <typename T, typename = typename std::enable_if< |
| 53 | base::is_trivially_copyable<T>::value>::type> |
| 54 | T* NewArray(size_t size, T default_val) { |
| 55 | T* result = reinterpret_cast<T*>(NewArray<uint8_t>(sizeof(T) * size)); |
| 56 | for (size_t i = 0; i < size; ++i) result[i] = default_val; |
| 57 | return result; |
| 58 | } |
| 59 | |
| 60 | template <typename T> |
| 61 | void DeleteArray(T* array) { |
| 62 | delete[] array; |
| 63 | } |
| 64 | |
| 65 | template <typename T> |
| 66 | struct ArrayDeleter { |
| 67 | void operator()(T* array) { DeleteArray(array); } |
| 68 | }; |
| 69 | |
| 70 | template <typename T> |
| 71 | using ArrayUniquePtr = std::unique_ptr<T, ArrayDeleter<T>>; |
| 72 | |
| 73 | // The normal strdup functions use malloc. These versions of StrDup |
| 74 | // and StrNDup uses new and calls the FatalProcessOutOfMemory handler |
| 75 | // if allocation fails. |
| 76 | V8_EXPORT_PRIVATE char* StrDup(const char* str); |
| 77 | char* StrNDup(const char* str, int n); |
| 78 | |
| 79 | // Allocation policy for allocating in the C free store using malloc |
| 80 | // and free. Used as the default policy for lists. |
| 81 | class FreeStoreAllocationPolicy { |
| 82 | public: |
| 83 | template <typename T, typename TypeTag = T[]> |
| 84 | V8_INLINEinline __attribute__((always_inline)) T* NewArray(size_t length) { |
| 85 | return static_cast<T*>(Malloced::operator new(length * sizeof(T))); |
| 86 | } |
| 87 | template <typename T, typename TypeTag = T[]> |
| 88 | V8_INLINEinline __attribute__((always_inline)) void DeleteArray(T* p, size_t length) { |
| 89 | Malloced::operator delete(p); |
| 90 | } |
| 91 | }; |
| 92 | |
| 93 | using MallocFn = void* (*)(size_t); |
| 94 | |
| 95 | // Performs a malloc, with retry logic on failure. Returns nullptr on failure. |
| 96 | // Call free to release memory allocated with this function. |
| 97 | void* AllocWithRetry(size_t size, MallocFn = base::Malloc); |
| 98 | |
| 99 | V8_EXPORT_PRIVATE void* AlignedAlloc(size_t size, size_t alignment); |
| 100 | V8_EXPORT_PRIVATE void AlignedFree(void* ptr); |
| 101 | |
| 102 | // Returns platfrom page allocator instance. Guaranteed to be a valid pointer. |
| 103 | V8_EXPORT_PRIVATE v8::PageAllocator* GetPlatformPageAllocator(); |
| 104 | |
| 105 | // Returns platfrom virtual memory space instance. Guaranteed to be a valid |
| 106 | // pointer. |
| 107 | V8_EXPORT_PRIVATE v8::VirtualAddressSpace* GetPlatformVirtualAddressSpace(); |
| 108 | |
| 109 | #ifdef V8_SANDBOX |
| 110 | // Returns the page allocator instance for allocating pages inside the sandbox. |
| 111 | // Guaranteed to be a valid pointer. |
| 112 | V8_EXPORT_PRIVATE v8::PageAllocator* GetSandboxPageAllocator(); |
| 113 | #endif |
| 114 | |
| 115 | // Returns the appropriate page allocator to use for ArrayBuffer backing |
| 116 | // stores. If the sandbox is enabled, these must be allocated inside the |
| 117 | // sandbox and so this will be the SandboxPageAllocator. Otherwise it will be |
| 118 | // the PlatformPageAllocator. |
| 119 | inline v8::PageAllocator* GetArrayBufferPageAllocator() { |
| 120 | #ifdef V8_SANDBOX |
| 121 | return GetSandboxPageAllocator(); |
| 122 | #else |
| 123 | return GetPlatformPageAllocator(); |
| 124 | #endif |
| 125 | } |
| 126 | |
| 127 | // Sets the given page allocator as the platform page allocator and returns |
| 128 | // the current one. This function *must* be used only for testing purposes. |
| 129 | // It is not thread-safe and the testing infrastructure should ensure that |
| 130 | // the tests do not modify the value simultaneously. |
| 131 | V8_EXPORT_PRIVATE v8::PageAllocator* SetPlatformPageAllocatorForTesting( |
| 132 | v8::PageAllocator* page_allocator); |
| 133 | |
| 134 | // Gets the page granularity for AllocatePages and FreePages. Addresses returned |
| 135 | // by AllocatePages are aligned to this size. |
| 136 | V8_EXPORT_PRIVATE size_t AllocatePageSize(); |
| 137 | |
| 138 | // Gets the granularity at which the permissions and release calls can be made. |
| 139 | V8_EXPORT_PRIVATE size_t CommitPageSize(); |
| 140 | |
| 141 | // Sets the random seed so that GetRandomMmapAddr() will generate repeatable |
| 142 | // sequences of random mmap addresses. |
| 143 | V8_EXPORT_PRIVATE void SetRandomMmapSeed(int64_t seed); |
| 144 | |
| 145 | // Generate a random address to be used for hinting allocation calls. |
| 146 | V8_EXPORT_PRIVATE void* GetRandomMmapAddr(); |
| 147 | |
| 148 | // Allocates memory. Permissions are set according to the access argument. |
| 149 | // |address| is a hint. |size| and |alignment| must be multiples of |
| 150 | // AllocatePageSize(). Returns the address of the allocated memory, with the |
| 151 | // specified size and alignment, or nullptr on failure. |
| 152 | V8_EXPORT_PRIVATE |
| 153 | V8_WARN_UNUSED_RESULT__attribute__((warn_unused_result)) void* AllocatePages(v8::PageAllocator* page_allocator, |
| 154 | void* address, size_t size, |
| 155 | size_t alignment, |
| 156 | PageAllocator::Permission access); |
| 157 | |
| 158 | // Frees memory allocated by a call to AllocatePages. |address| and |size| must |
| 159 | // be multiples of AllocatePageSize(). |
| 160 | V8_EXPORT_PRIVATE |
| 161 | void FreePages(v8::PageAllocator* page_allocator, void* address, |
| 162 | const size_t size); |
| 163 | |
| 164 | // Releases memory that is no longer needed. The range specified by |address| |
| 165 | // and |size| must be an allocated memory region. |size| and |new_size| must be |
| 166 | // multiples of CommitPageSize(). Memory from |new_size| to |size| is released. |
| 167 | // Released memory is left in an undefined state, so it should not be accessed. |
| 168 | V8_EXPORT_PRIVATE |
| 169 | void ReleasePages(v8::PageAllocator* page_allocator, void* address, size_t size, |
| 170 | size_t new_size); |
| 171 | |
| 172 | // Sets permissions according to |access|. |address| and |size| must be |
| 173 | // multiples of CommitPageSize(). Setting permission to kNoAccess may |
| 174 | // cause the memory contents to be lost. Returns true on success, otherwise |
| 175 | // false. |
| 176 | V8_EXPORT_PRIVATE |
| 177 | V8_WARN_UNUSED_RESULT__attribute__((warn_unused_result)) bool SetPermissions(v8::PageAllocator* page_allocator, |
| 178 | void* address, size_t size, |
| 179 | PageAllocator::Permission access); |
| 180 | inline bool SetPermissions(v8::PageAllocator* page_allocator, Address address, |
| 181 | size_t size, PageAllocator::Permission access) { |
| 182 | return SetPermissions(page_allocator, reinterpret_cast<void*>(address), size, |
| 183 | access); |
| 184 | } |
| 185 | |
| 186 | // Function that may release reserved memory regions to allow failed allocations |
| 187 | // to succeed. |length| is the amount of memory needed. Returns |true| if memory |
| 188 | // could be released, false otherwise. |
| 189 | V8_EXPORT_PRIVATE bool OnCriticalMemoryPressure(size_t length); |
| 190 | |
| 191 | // Represents and controls an area of reserved memory. |
| 192 | class VirtualMemory final { |
| 193 | public: |
| 194 | enum JitPermission { kNoJit, kMapAsJittable }; |
| 195 | |
| 196 | // Empty VirtualMemory object, controlling no reserved memory. |
| 197 | V8_EXPORT_PRIVATE VirtualMemory(); |
| 198 | |
| 199 | VirtualMemory(const VirtualMemory&) = delete; |
| 200 | VirtualMemory& operator=(const VirtualMemory&) = delete; |
| 201 | |
| 202 | // Reserves virtual memory containing an area of the given size that is |
| 203 | // aligned per |alignment| rounded up to the |page_allocator|'s allocate page |
| 204 | // size. The |size| must be aligned with |page_allocator|'s commit page size. |
| 205 | // This may not be at the position returned by address(). |
| 206 | V8_EXPORT_PRIVATE VirtualMemory(v8::PageAllocator* page_allocator, |
| 207 | size_t size, void* hint, size_t alignment = 1, |
| 208 | JitPermission jit = kNoJit); |
| 209 | |
| 210 | // Construct a virtual memory by assigning it some already mapped address |
| 211 | // and size. |
| 212 | VirtualMemory(v8::PageAllocator* page_allocator, Address address, size_t size) |
| 213 | : page_allocator_(page_allocator), region_(address, size) { |
| 214 | DCHECK_NOT_NULL(page_allocator)((void) 0); |
| 215 | DCHECK(IsAligned(address, page_allocator->AllocatePageSize()))((void) 0); |
| 216 | DCHECK(IsAligned(size, page_allocator->CommitPageSize()))((void) 0); |
| 217 | } |
| 218 | |
| 219 | // Releases the reserved memory, if any, controlled by this VirtualMemory |
| 220 | // object. |
| 221 | V8_EXPORT_PRIVATE ~VirtualMemory(); |
| 222 | |
| 223 | // Move constructor. |
| 224 | VirtualMemory(VirtualMemory&& other) V8_NOEXCEPTnoexcept { *this = std::move(other); } |
| 225 | |
| 226 | // Move assignment operator. |
| 227 | VirtualMemory& operator=(VirtualMemory&& other) V8_NOEXCEPTnoexcept { |
| 228 | DCHECK(!IsReserved())((void) 0); |
| 229 | page_allocator_ = other.page_allocator_; |
| 230 | region_ = other.region_; |
| 231 | other.Reset(); |
| 232 | return *this; |
| 233 | } |
| 234 | |
| 235 | // Returns whether the memory has been reserved. |
| 236 | bool IsReserved() const { return region_.begin() != kNullAddress; } |
| 237 | |
| 238 | // Initialize or resets an embedded VirtualMemory object. |
| 239 | V8_EXPORT_PRIVATE void Reset(); |
| 240 | |
| 241 | v8::PageAllocator* page_allocator() { return page_allocator_; } |
| 242 | |
| 243 | const base::AddressRegion& region() const { return region_; } |
| 244 | |
| 245 | // Returns the start address of the reserved memory. |
| 246 | // If the memory was reserved with an alignment, this address is not |
| 247 | // necessarily aligned. The user might need to round it up to a multiple of |
| 248 | // the alignment to get the start of the aligned block. |
| 249 | Address address() const { |
| 250 | DCHECK(IsReserved())((void) 0); |
| 251 | return region_.begin(); |
| 252 | } |
| 253 | |
| 254 | Address end() const { |
| 255 | DCHECK(IsReserved())((void) 0); |
| 256 | return region_.end(); |
| 257 | } |
| 258 | |
| 259 | // Returns the size of the reserved memory. The returned value is only |
| 260 | // meaningful when IsReserved() returns true. |
| 261 | // If the memory was reserved with an alignment, this size may be larger |
| 262 | // than the requested size. |
| 263 | size_t size() const { return region_.size(); } |
| 264 | |
| 265 | // Sets permissions according to the access argument. address and size must be |
| 266 | // multiples of CommitPageSize(). Returns true on success, otherwise false. |
| 267 | V8_EXPORT_PRIVATE bool SetPermissions(Address address, size_t size, |
| 268 | PageAllocator::Permission access); |
| 269 | |
| 270 | // Releases memory after |free_start|. Returns the number of bytes released. |
| 271 | V8_EXPORT_PRIVATE size_t Release(Address free_start); |
| 272 | |
| 273 | // Frees all memory. |
| 274 | V8_EXPORT_PRIVATE void Free(); |
| 275 | |
| 276 | // As with Free but does not write to the VirtualMemory object itself so it |
| 277 | // can be called on a VirtualMemory that is itself not writable. |
| 278 | V8_EXPORT_PRIVATE void FreeReadOnly(); |
| 279 | |
| 280 | bool InVM(Address address, size_t size) const { |
| 281 | return region_.contains(address, size); |
| 282 | } |
| 283 | |
| 284 | private: |
| 285 | // Page allocator that controls the virtual memory. |
| 286 | v8::PageAllocator* page_allocator_ = nullptr; |
| 287 | base::AddressRegion region_; |
| 288 | }; |
| 289 | |
| 290 | // Represents a VirtualMemory reservation along with a BoundedPageAllocator that |
| 291 | // can be used to allocate within the reservation. |
| 292 | // |
| 293 | // Virtual memory cages are used for both the pointer compression cage and code |
| 294 | // ranges (on platforms that require code ranges) and are configurable via |
| 295 | // ReservationParams. |
| 296 | // |
| 297 | // +------------+-----------+------------ ~~~ --+- ~~~ -+ |
| 298 | // | ... | ... | ... | ... | |
| 299 | // +------------+-----------+------------ ~~~ --+- ~~~ -+ |
| 300 | // ^ ^ ^ |
| 301 | // start cage base allocatable base |
| 302 | // |
| 303 | // <------------> <-------------------> |
| 304 | // base bias size allocatable size |
| 305 | // <-------------------------------> |
| 306 | // cage size |
| 307 | // <----------------------------------------------------> |
| 308 | // reservation size |
| 309 | // |
| 310 | // - The reservation is made using ReservationParams::page_allocator. |
| 311 | // - start is the start of the virtual memory reservation. |
| 312 | // - cage base is the base address of the cage. |
| 313 | // - allocatable base is the cage base rounded up to the nearest |
| 314 | // ReservationParams::page_size, and is the start of the allocatable area for |
| 315 | // the BoundedPageAllocator. |
| 316 | // - cage size is the size of the area from cage base to the end of the |
| 317 | // allocatable area. |
| 318 | // |
| 319 | // - The base bias is configured by ReservationParams::base_bias_size. |
| 320 | // - The reservation size is configured by ReservationParams::reservation_size |
| 321 | // but it might be actually bigger if we end up over-reserving the virtual |
| 322 | // address space. |
| 323 | // |
| 324 | // Additionally, |
| 325 | // - The alignment of the cage base is configured by |
| 326 | // ReservationParams::base_alignment. |
| 327 | // - The page size of the BoundedPageAllocator is configured by |
| 328 | // ReservationParams::page_size. |
| 329 | // - A hint for the value of start can be passed by |
| 330 | // ReservationParams::requested_start_hint. |
| 331 | // |
| 332 | // The configuration is subject to the following alignment requirements. |
| 333 | // Below, AllocatePageSize is short for |
| 334 | // ReservationParams::page_allocator->AllocatePageSize(). |
| 335 | // |
| 336 | // - The reservation size must be AllocatePageSize-aligned. |
| 337 | // - If the base alignment is not kAnyBaseAlignment, both the base alignment |
| 338 | // and the base bias size must be AllocatePageSize-aligned. |
| 339 | // - The base alignment may be kAnyBaseAlignment to denote any alignment is |
| 340 | // acceptable. In this case the base bias size does not need to be aligned. |
| 341 | class VirtualMemoryCage { |
| 342 | public: |
| 343 | VirtualMemoryCage(); |
| 344 | virtual ~VirtualMemoryCage(); |
| 345 | |
| 346 | VirtualMemoryCage(const VirtualMemoryCage&) = delete; |
| 347 | VirtualMemoryCage& operator=(VirtualMemoryCage&) = delete; |
| 348 | |
| 349 | VirtualMemoryCage(VirtualMemoryCage&& other) V8_NOEXCEPTnoexcept; |
| 350 | VirtualMemoryCage& operator=(VirtualMemoryCage&& other) V8_NOEXCEPTnoexcept; |
| 351 | |
| 352 | Address base() const { return base_; } |
| 353 | size_t size() const { return size_; } |
| 354 | |
| 355 | base::BoundedPageAllocator* page_allocator() const { |
| 356 | return page_allocator_.get(); |
| 357 | } |
| 358 | |
| 359 | VirtualMemory* reservation() { return &reservation_; } |
| 360 | const VirtualMemory* reservation() const { return &reservation_; } |
| 361 | |
| 362 | bool IsReserved() const { |
| 363 | DCHECK_EQ(base_ != kNullAddress, reservation_.IsReserved())((void) 0); |
| 364 | DCHECK_EQ(base_ != kNullAddress, size_ != 0)((void) 0); |
| 365 | return reservation_.IsReserved(); |
| 366 | } |
| 367 | |
| 368 | struct ReservationParams { |
| 369 | // The allocator to use to reserve the virtual memory. |
| 370 | v8::PageAllocator* page_allocator; |
| 371 | // See diagram above. |
| 372 | size_t reservation_size; |
| 373 | size_t base_alignment; |
| 374 | size_t base_bias_size; |
| 375 | size_t page_size; |
| 376 | Address requested_start_hint; |
| 377 | |
| 378 | static constexpr size_t kAnyBaseAlignment = 1; |
| 379 | }; |
| 380 | |
| 381 | // A number of attempts is made to try to reserve a region that satisfies the |
| 382 | // constraints in params, but this may fail. The base address may be different |
| 383 | // than the one requested. |
| 384 | // If an existing reservation is provided, it will be used for this cage |
| 385 | // instead. The caller retains ownership of the reservation and is responsible |
| 386 | // for keeping the memory reserved during the lifetime of this object. |
| 387 | bool InitReservation( |
| 388 | const ReservationParams& params, |
| 389 | base::AddressRegion existing_reservation = base::AddressRegion()); |
| 390 | |
| 391 | void Free(); |
| 392 | |
| 393 | protected: |
| 394 | Address base_ = kNullAddress; |
| 395 | size_t size_ = 0; |
| 396 | std::unique_ptr<base::BoundedPageAllocator> page_allocator_; |
| 397 | VirtualMemory reservation_; |
| 398 | }; |
| 399 | |
| 400 | } // namespace internal |
| 401 | } // namespace v8 |
| 402 | |
| 403 | #endif // V8_UTILS_ALLOCATION_H_ |