1 // Copyright (c) 2015 The Chromium 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 "base/metrics/persistent_memory_allocator.h" 6 7 #include <assert.h> 8 #include <algorithm> 9 10 #if defined(OS_WIN) 11 #include "winbase.h" 12 #elif defined(OS_POSIX) 13 #include <sys/mman.h> 14 #endif 15 16 #include "base/files/memory_mapped_file.h" 17 #include "base/logging.h" 18 #include "base/memory/shared_memory.h" 19 #include "base/metrics/histogram_macros.h" 20 21 namespace { 22 23 // Limit of memory segment size. It has to fit in an unsigned 32-bit number 24 // and should be a power of 2 in order to accomodate almost any page size. 25 const uint32_t kSegmentMaxSize = 1 << 30; // 1 GiB 26 27 // A constant (random) value placed in the shared metadata to identify 28 // an already initialized memory segment. 29 const uint32_t kGlobalCookie = 0x408305DC; 30 31 // The current version of the metadata. If updates are made that change 32 // the metadata, the version number can be queried to operate in a backward- 33 // compatible manner until the memory segment is completely re-initalized. 34 const uint32_t kGlobalVersion = 1; 35 36 // Constant values placed in the block headers to indicate its state. 37 const uint32_t kBlockCookieFree = 0; 38 const uint32_t kBlockCookieQueue = 1; 39 const uint32_t kBlockCookieWasted = (uint32_t)-1; 40 const uint32_t kBlockCookieAllocated = 0xC8799269; 41 42 // TODO(bcwhite): When acceptable, consider moving flags to std::atomic<char> 43 // types rather than combined bitfield. 44 45 // Flags stored in the flags_ field of the SharedMetaData structure below. 46 enum : int { 47 kFlagCorrupt = 1 << 0, 48 kFlagFull = 1 << 1 49 }; 50 51 bool CheckFlag(const volatile std::atomic<uint32_t>* flags, int flag) { 52 uint32_t loaded_flags = flags->load(std::memory_order_relaxed); 53 return (loaded_flags & flag) != 0; 54 } 55 56 void SetFlag(volatile std::atomic<uint32_t>* flags, int flag) { 57 uint32_t loaded_flags = flags->load(std::memory_order_relaxed); 58 for (;;) { 59 uint32_t new_flags = (loaded_flags & ~flag) | flag; 60 // In the failue case, actual "flags" value stored in loaded_flags. 61 if (flags->compare_exchange_weak(loaded_flags, new_flags)) 62 break; 63 } 64 } 65 66 } // namespace 67 68 namespace base { 69 70 // All allocations and data-structures must be aligned to this byte boundary. 71 // Alignment as large as the physical bus between CPU and RAM is _required_ 72 // for some architectures, is simply more efficient on other CPUs, and 73 // generally a Good Idea(tm) for all platforms as it reduces/eliminates the 74 // chance that a type will span cache lines. Alignment mustn't be less 75 // than 8 to ensure proper alignment for all types. The rest is a balance 76 // between reducing spans across multiple cache lines and wasted space spent 77 // padding out allocations. An alignment of 16 would ensure that the block 78 // header structure always sits in a single cache line. An average of about 79 // 1/2 this value will be wasted with every allocation. 80 const uint32_t PersistentMemoryAllocator::kAllocAlignment = 8; 81 82 // The block-header is placed at the top of every allocation within the 83 // segment to describe the data that follows it. 84 struct PersistentMemoryAllocator::BlockHeader { 85 uint32_t size; // Number of bytes in this block, including header. 86 uint32_t cookie; // Constant value indicating completed allocation. 87 std::atomic<uint32_t> type_id; // Arbitrary number indicating data type. 88 std::atomic<uint32_t> next; // Pointer to the next block when iterating. 89 }; 90 91 // The shared metadata exists once at the top of the memory segment to 92 // describe the state of the allocator to all processes. 93 struct PersistentMemoryAllocator::SharedMetadata { 94 uint32_t cookie; // Some value that indicates complete initialization. 95 uint32_t size; // Total size of memory segment. 96 uint32_t page_size; // Paging size within memory segment. 97 uint32_t version; // Version code so upgrades don't break. 98 uint64_t id; // Arbitrary ID number given by creator. 99 uint32_t name; // Reference to stored name string. 100 101 // Above is read-only after first construction. Below may be changed and 102 // so must be marked "volatile" to provide correct inter-process behavior. 103 104 // Bitfield of information flags. Access to this should be done through 105 // the CheckFlag() and SetFlag() methods defined above. 106 volatile std::atomic<uint32_t> flags; 107 108 // Offset/reference to first free space in segment. 109 volatile std::atomic<uint32_t> freeptr; 110 111 // The "iterable" queue is an M&S Queue as described here, append-only: 112 // https://www.research.ibm.com/people/m/michael/podc-1996.pdf 113 volatile std::atomic<uint32_t> tailptr; // Last block of iteration queue. 114 volatile BlockHeader queue; // Empty block for linked-list head/tail. 115 }; 116 117 // The "queue" block header is used to detect "last node" so that zero/null 118 // can be used to indicate that it hasn't been added at all. It is part of 119 // the SharedMetadata structure which itself is always located at offset zero. 120 const PersistentMemoryAllocator::Reference 121 PersistentMemoryAllocator::kReferenceQueue = 122 offsetof(SharedMetadata, queue); 123 124 const base::FilePath::CharType PersistentMemoryAllocator::kFileExtension[] = 125 FILE_PATH_LITERAL(".pma"); 126 127 128 PersistentMemoryAllocator::Iterator::Iterator( 129 const PersistentMemoryAllocator* allocator) 130 : allocator_(allocator), last_record_(kReferenceQueue), record_count_(0) {} 131 132 PersistentMemoryAllocator::Iterator::Iterator( 133 const PersistentMemoryAllocator* allocator, 134 Reference starting_after) 135 : allocator_(allocator), last_record_(starting_after), record_count_(0) { 136 // Ensure that the starting point is a valid, iterable block (meaning it can 137 // be read and has a non-zero "next" pointer). 138 const volatile BlockHeader* block = 139 allocator_->GetBlock(starting_after, 0, 0, false, false); 140 if (!block || block->next.load(std::memory_order_relaxed) == 0) { 141 NOTREACHED(); 142 last_record_.store(kReferenceQueue, std::memory_order_release); 143 } 144 } 145 146 PersistentMemoryAllocator::Reference 147 PersistentMemoryAllocator::Iterator::GetNext(uint32_t* type_return) { 148 // Make a copy of the existing count of found-records, acquiring all changes 149 // made to the allocator, notably "freeptr" (see comment in loop for why 150 // the load of that value cannot be moved above here) that occurred during 151 // any previous runs of this method, including those by parallel threads 152 // that interrupted it. It pairs with the Release at the end of this method. 153 // 154 // Otherwise, if the compiler were to arrange the two loads such that 155 // "count" was fetched _after_ "freeptr" then it would be possible for 156 // this thread to be interrupted between them and other threads perform 157 // multiple allocations, make-iterables, and iterations (with the included 158 // increment of |record_count_|) culminating in the check at the bottom 159 // mistakenly determining that a loop exists. Isn't this stuff fun? 160 uint32_t count = record_count_.load(std::memory_order_acquire); 161 162 Reference last = last_record_.load(std::memory_order_acquire); 163 Reference next; 164 while (true) { 165 const volatile BlockHeader* block = 166 allocator_->GetBlock(last, 0, 0, true, false); 167 if (!block) // Invalid iterator state. 168 return kReferenceNull; 169 170 // The compiler and CPU can freely reorder all memory accesses on which 171 // there are no dependencies. It could, for example, move the load of 172 // "freeptr" to above this point because there are no explicit dependencies 173 // between it and "next". If it did, however, then another block could 174 // be queued after that but before the following load meaning there is 175 // one more queued block than the future "detect loop by having more 176 // blocks that could fit before freeptr" will allow. 177 // 178 // By "acquiring" the "next" value here, it's synchronized to the enqueue 179 // of the node which in turn is synchronized to the allocation (which sets 180 // freeptr). Thus, the scenario above cannot happen. 181 next = block->next.load(std::memory_order_acquire); 182 if (next == kReferenceQueue) // No next allocation in queue. 183 return kReferenceNull; 184 block = allocator_->GetBlock(next, 0, 0, false, false); 185 if (!block) { // Memory is corrupt. 186 allocator_->SetCorrupt(); 187 return kReferenceNull; 188 } 189 190 // Update the "last_record" pointer to be the reference being returned. 191 // If it fails then another thread has already iterated past it so loop 192 // again. Failing will also load the existing value into "last" so there 193 // is no need to do another such load when the while-loop restarts. A 194 // "strong" compare-exchange is used because failing unnecessarily would 195 // mean repeating some fairly costly validations above. 196 if (last_record_.compare_exchange_strong(last, next)) { 197 *type_return = block->type_id.load(std::memory_order_relaxed); 198 break; 199 } 200 } 201 202 // Memory corruption could cause a loop in the list. Such must be detected 203 // so as to not cause an infinite loop in the caller. This is done by simply 204 // making sure it doesn't iterate more times than the absolute maximum 205 // number of allocations that could have been made. Callers are likely 206 // to loop multiple times before it is detected but at least it stops. 207 const uint32_t freeptr = std::min( 208 allocator_->shared_meta()->freeptr.load(std::memory_order_relaxed), 209 allocator_->mem_size_); 210 const uint32_t max_records = 211 freeptr / (sizeof(BlockHeader) + kAllocAlignment); 212 if (count > max_records) { 213 allocator_->SetCorrupt(); 214 return kReferenceNull; 215 } 216 217 // Increment the count and release the changes made above. It pairs with 218 // the Acquire at the top of this method. Note that this operation is not 219 // strictly synchonized with fetching of the object to return, which would 220 // have to be done inside the loop and is somewhat complicated to achieve. 221 // It does not matter if it falls behind temporarily so long as it never 222 // gets ahead. 223 record_count_.fetch_add(1, std::memory_order_release); 224 return next; 225 } 226 227 PersistentMemoryAllocator::Reference 228 PersistentMemoryAllocator::Iterator::GetNextOfType(uint32_t type_match) { 229 Reference ref; 230 uint32_t type_found; 231 while ((ref = GetNext(&type_found)) != 0) { 232 if (type_found == type_match) 233 return ref; 234 } 235 return kReferenceNull; 236 } 237 238 239 // static 240 bool PersistentMemoryAllocator::IsMemoryAcceptable(const void* base, 241 size_t size, 242 size_t page_size, 243 bool readonly) { 244 return ((base && reinterpret_cast<uintptr_t>(base) % kAllocAlignment == 0) && 245 (size >= sizeof(SharedMetadata) && size <= kSegmentMaxSize) && 246 (size % kAllocAlignment == 0 || readonly) && 247 (page_size == 0 || size % page_size == 0 || readonly)); 248 } 249 250 PersistentMemoryAllocator::PersistentMemoryAllocator( 251 void* base, 252 size_t size, 253 size_t page_size, 254 uint64_t id, 255 base::StringPiece name, 256 bool readonly) 257 : mem_base_(static_cast<char*>(base)), 258 mem_size_(static_cast<uint32_t>(size)), 259 mem_page_(static_cast<uint32_t>((page_size ? page_size : size))), 260 readonly_(readonly), 261 corrupt_(0), 262 allocs_histogram_(nullptr), 263 used_histogram_(nullptr) { 264 static_assert(sizeof(BlockHeader) % kAllocAlignment == 0, 265 "BlockHeader is not a multiple of kAllocAlignment"); 266 static_assert(sizeof(SharedMetadata) % kAllocAlignment == 0, 267 "SharedMetadata is not a multiple of kAllocAlignment"); 268 static_assert(kReferenceQueue % kAllocAlignment == 0, 269 "\"queue\" is not aligned properly; must be at end of struct"); 270 271 // Ensure that memory segment is of acceptable size. 272 CHECK(IsMemoryAcceptable(base, size, page_size, readonly)); 273 274 // These atomics operate inter-process and so must be lock-free. The local 275 // casts are to make sure it can be evaluated at compile time to a constant. 276 CHECK(((SharedMetadata*)0)->freeptr.is_lock_free()); 277 CHECK(((SharedMetadata*)0)->flags.is_lock_free()); 278 CHECK(((BlockHeader*)0)->next.is_lock_free()); 279 CHECK(corrupt_.is_lock_free()); 280 281 if (shared_meta()->cookie != kGlobalCookie) { 282 if (readonly) { 283 SetCorrupt(); 284 return; 285 } 286 287 // This block is only executed when a completely new memory segment is 288 // being initialized. It's unshared and single-threaded... 289 volatile BlockHeader* const first_block = 290 reinterpret_cast<volatile BlockHeader*>(mem_base_ + 291 sizeof(SharedMetadata)); 292 if (shared_meta()->cookie != 0 || 293 shared_meta()->size != 0 || 294 shared_meta()->version != 0 || 295 shared_meta()->freeptr.load(std::memory_order_relaxed) != 0 || 296 shared_meta()->flags.load(std::memory_order_relaxed) != 0 || 297 shared_meta()->id != 0 || 298 shared_meta()->name != 0 || 299 shared_meta()->tailptr != 0 || 300 shared_meta()->queue.cookie != 0 || 301 shared_meta()->queue.next.load(std::memory_order_relaxed) != 0 || 302 first_block->size != 0 || 303 first_block->cookie != 0 || 304 first_block->type_id.load(std::memory_order_relaxed) != 0 || 305 first_block->next != 0) { 306 // ...or something malicious has been playing with the metadata. 307 SetCorrupt(); 308 } 309 310 // This is still safe to do even if corruption has been detected. 311 shared_meta()->cookie = kGlobalCookie; 312 shared_meta()->size = mem_size_; 313 shared_meta()->page_size = mem_page_; 314 shared_meta()->version = kGlobalVersion; 315 shared_meta()->id = id; 316 shared_meta()->freeptr.store(sizeof(SharedMetadata), 317 std::memory_order_release); 318 319 // Set up the queue of iterable allocations. 320 shared_meta()->queue.size = sizeof(BlockHeader); 321 shared_meta()->queue.cookie = kBlockCookieQueue; 322 shared_meta()->queue.next.store(kReferenceQueue, std::memory_order_release); 323 shared_meta()->tailptr.store(kReferenceQueue, std::memory_order_release); 324 325 // Allocate space for the name so other processes can learn it. 326 if (!name.empty()) { 327 const size_t name_length = name.length() + 1; 328 shared_meta()->name = Allocate(name_length, 0); 329 char* name_cstr = GetAsObject<char>(shared_meta()->name, 0); 330 if (name_cstr) 331 memcpy(name_cstr, name.data(), name.length()); 332 } 333 } else { 334 if (shared_meta()->size == 0 || 335 shared_meta()->version == 0 || 336 shared_meta()->freeptr.load(std::memory_order_relaxed) == 0 || 337 shared_meta()->tailptr == 0 || 338 shared_meta()->queue.cookie == 0 || 339 shared_meta()->queue.next.load(std::memory_order_relaxed) == 0) { 340 SetCorrupt(); 341 } 342 if (!readonly) { 343 // The allocator is attaching to a previously initialized segment of 344 // memory. If the initialization parameters differ, make the best of it 345 // by reducing the local construction parameters to match those of 346 // the actual memory area. This ensures that the local object never 347 // tries to write outside of the original bounds. 348 // Because the fields are const to ensure that no code other than the 349 // constructor makes changes to them as well as to give optimization 350 // hints to the compiler, it's necessary to const-cast them for changes 351 // here. 352 if (shared_meta()->size < mem_size_) 353 *const_cast<uint32_t*>(&mem_size_) = shared_meta()->size; 354 if (shared_meta()->page_size < mem_page_) 355 *const_cast<uint32_t*>(&mem_page_) = shared_meta()->page_size; 356 357 // Ensure that settings are still valid after the above adjustments. 358 if (!IsMemoryAcceptable(base, mem_size_, mem_page_, readonly)) 359 SetCorrupt(); 360 } 361 } 362 } 363 364 PersistentMemoryAllocator::~PersistentMemoryAllocator() { 365 // It's strictly forbidden to do any memory access here in case there is 366 // some issue with the underlying memory segment. The "Local" allocator 367 // makes use of this to allow deletion of the segment on the heap from 368 // within its destructor. 369 } 370 371 uint64_t PersistentMemoryAllocator::Id() const { 372 return shared_meta()->id; 373 } 374 375 const char* PersistentMemoryAllocator::Name() const { 376 Reference name_ref = shared_meta()->name; 377 const char* name_cstr = GetAsObject<char>(name_ref, 0); 378 if (!name_cstr) 379 return ""; 380 381 size_t name_length = GetAllocSize(name_ref); 382 if (name_cstr[name_length - 1] != '\0') { 383 NOTREACHED(); 384 SetCorrupt(); 385 return ""; 386 } 387 388 return name_cstr; 389 } 390 391 void PersistentMemoryAllocator::CreateTrackingHistograms( 392 base::StringPiece name) { 393 if (name.empty() || readonly_) 394 return; 395 396 std::string name_string = name.as_string(); 397 DCHECK(!used_histogram_); 398 used_histogram_ = LinearHistogram::FactoryGet( 399 "UMA.PersistentAllocator." + name_string + ".UsedPct", 1, 101, 21, 400 HistogramBase::kUmaTargetedHistogramFlag); 401 402 DCHECK(!allocs_histogram_); 403 allocs_histogram_ = Histogram::FactoryGet( 404 "UMA.PersistentAllocator." + name_string + ".Allocs", 1, 10000, 50, 405 HistogramBase::kUmaTargetedHistogramFlag); 406 } 407 408 size_t PersistentMemoryAllocator::used() const { 409 return std::min(shared_meta()->freeptr.load(std::memory_order_relaxed), 410 mem_size_); 411 } 412 413 size_t PersistentMemoryAllocator::GetAllocSize(Reference ref) const { 414 const volatile BlockHeader* const block = GetBlock(ref, 0, 0, false, false); 415 if (!block) 416 return 0; 417 uint32_t size = block->size; 418 // Header was verified by GetBlock() but a malicious actor could change 419 // the value between there and here. Check it again. 420 if (size <= sizeof(BlockHeader) || ref + size > mem_size_) { 421 SetCorrupt(); 422 return 0; 423 } 424 return size - sizeof(BlockHeader); 425 } 426 427 uint32_t PersistentMemoryAllocator::GetType(Reference ref) const { 428 const volatile BlockHeader* const block = GetBlock(ref, 0, 0, false, false); 429 if (!block) 430 return 0; 431 return block->type_id.load(std::memory_order_relaxed); 432 } 433 434 bool PersistentMemoryAllocator::ChangeType(Reference ref, 435 uint32_t to_type_id, 436 uint32_t from_type_id) { 437 DCHECK(!readonly_); 438 volatile BlockHeader* const block = GetBlock(ref, 0, 0, false, false); 439 if (!block) 440 return false; 441 442 // This is a "strong" exchange because there is no loop that can retry in 443 // the wake of spurious failures possible with "weak" exchanges. 444 return block->type_id.compare_exchange_strong(from_type_id, to_type_id); 445 } 446 447 PersistentMemoryAllocator::Reference PersistentMemoryAllocator::Allocate( 448 size_t req_size, 449 uint32_t type_id) { 450 Reference ref = AllocateImpl(req_size, type_id); 451 if (ref) { 452 // Success: Record this allocation in usage stats (if active). 453 if (allocs_histogram_) 454 allocs_histogram_->Add(static_cast<HistogramBase::Sample>(req_size)); 455 } else { 456 // Failure: Record an allocation of zero for tracking. 457 if (allocs_histogram_) 458 allocs_histogram_->Add(0); 459 } 460 return ref; 461 } 462 463 PersistentMemoryAllocator::Reference PersistentMemoryAllocator::AllocateImpl( 464 size_t req_size, 465 uint32_t type_id) { 466 DCHECK(!readonly_); 467 468 // Validate req_size to ensure it won't overflow when used as 32-bit value. 469 if (req_size > kSegmentMaxSize - sizeof(BlockHeader)) { 470 NOTREACHED(); 471 return kReferenceNull; 472 } 473 474 // Round up the requested size, plus header, to the next allocation alignment. 475 uint32_t size = static_cast<uint32_t>(req_size + sizeof(BlockHeader)); 476 size = (size + (kAllocAlignment - 1)) & ~(kAllocAlignment - 1); 477 if (size <= sizeof(BlockHeader) || size > mem_page_) { 478 NOTREACHED(); 479 return kReferenceNull; 480 } 481 482 // Get the current start of unallocated memory. Other threads may 483 // update this at any time and cause us to retry these operations. 484 // This value should be treated as "const" to avoid confusion through 485 // the code below but recognize that any failed compare-exchange operation 486 // involving it will cause it to be loaded with a more recent value. The 487 // code should either exit or restart the loop in that case. 488 /* const */ uint32_t freeptr = 489 shared_meta()->freeptr.load(std::memory_order_acquire); 490 491 // Allocation is lockless so we do all our caculation and then, if saving 492 // indicates a change has occurred since we started, scrap everything and 493 // start over. 494 for (;;) { 495 if (IsCorrupt()) 496 return kReferenceNull; 497 498 if (freeptr + size > mem_size_) { 499 SetFlag(&shared_meta()->flags, kFlagFull); 500 return kReferenceNull; 501 } 502 503 // Get pointer to the "free" block. If something has been allocated since 504 // the load of freeptr above, it is still safe as nothing will be written 505 // to that location until after the compare-exchange below. 506 volatile BlockHeader* const block = GetBlock(freeptr, 0, 0, false, true); 507 if (!block) { 508 SetCorrupt(); 509 return kReferenceNull; 510 } 511 512 // An allocation cannot cross page boundaries. If it would, create a 513 // "wasted" block and begin again at the top of the next page. This 514 // area could just be left empty but we fill in the block header just 515 // for completeness sake. 516 const uint32_t page_free = mem_page_ - freeptr % mem_page_; 517 if (size > page_free) { 518 if (page_free <= sizeof(BlockHeader)) { 519 SetCorrupt(); 520 return kReferenceNull; 521 } 522 const uint32_t new_freeptr = freeptr + page_free; 523 if (shared_meta()->freeptr.compare_exchange_strong(freeptr, 524 new_freeptr)) { 525 block->size = page_free; 526 block->cookie = kBlockCookieWasted; 527 } 528 continue; 529 } 530 531 // Don't leave a slice at the end of a page too small for anything. This 532 // can result in an allocation up to two alignment-sizes greater than the 533 // minimum required by requested-size + header + alignment. 534 if (page_free - size < sizeof(BlockHeader) + kAllocAlignment) 535 size = page_free; 536 537 const uint32_t new_freeptr = freeptr + size; 538 if (new_freeptr > mem_size_) { 539 SetCorrupt(); 540 return kReferenceNull; 541 } 542 543 // Save our work. Try again if another thread has completed an allocation 544 // while we were processing. A "weak" exchange would be permissable here 545 // because the code will just loop and try again but the above processing 546 // is significant so make the extra effort of a "strong" exchange. 547 if (!shared_meta()->freeptr.compare_exchange_strong(freeptr, new_freeptr)) 548 continue; 549 550 // Given that all memory was zeroed before ever being given to an instance 551 // of this class and given that we only allocate in a monotomic fashion 552 // going forward, it must be that the newly allocated block is completely 553 // full of zeros. If we find anything in the block header that is NOT a 554 // zero then something must have previously run amuck through memory, 555 // writing beyond the allocated space and into unallocated space. 556 if (block->size != 0 || 557 block->cookie != kBlockCookieFree || 558 block->type_id.load(std::memory_order_relaxed) != 0 || 559 block->next.load(std::memory_order_relaxed) != 0) { 560 SetCorrupt(); 561 return kReferenceNull; 562 } 563 564 block->size = size; 565 block->cookie = kBlockCookieAllocated; 566 block->type_id.store(type_id, std::memory_order_relaxed); 567 return freeptr; 568 } 569 } 570 571 void PersistentMemoryAllocator::GetMemoryInfo(MemoryInfo* meminfo) const { 572 uint32_t remaining = std::max( 573 mem_size_ - shared_meta()->freeptr.load(std::memory_order_relaxed), 574 (uint32_t)sizeof(BlockHeader)); 575 meminfo->total = mem_size_; 576 meminfo->free = IsCorrupt() ? 0 : remaining - sizeof(BlockHeader); 577 } 578 579 void PersistentMemoryAllocator::MakeIterable(Reference ref) { 580 DCHECK(!readonly_); 581 if (IsCorrupt()) 582 return; 583 volatile BlockHeader* block = GetBlock(ref, 0, 0, false, false); 584 if (!block) // invalid reference 585 return; 586 if (block->next.load(std::memory_order_acquire) != 0) // Already iterable. 587 return; 588 block->next.store(kReferenceQueue, std::memory_order_release); // New tail. 589 590 // Try to add this block to the tail of the queue. May take multiple tries. 591 // If so, tail will be automatically updated with a more recent value during 592 // compare-exchange operations. 593 uint32_t tail = shared_meta()->tailptr.load(std::memory_order_acquire); 594 for (;;) { 595 // Acquire the current tail-pointer released by previous call to this 596 // method and validate it. 597 block = GetBlock(tail, 0, 0, true, false); 598 if (!block) { 599 SetCorrupt(); 600 return; 601 } 602 603 // Try to insert the block at the tail of the queue. The tail node always 604 // has an existing value of kReferenceQueue; if that is somehow not the 605 // existing value then another thread has acted in the meantime. A "strong" 606 // exchange is necessary so the "else" block does not get executed when 607 // that is not actually the case (which can happen with a "weak" exchange). 608 uint32_t next = kReferenceQueue; // Will get replaced with existing value. 609 if (block->next.compare_exchange_strong(next, ref, 610 std::memory_order_acq_rel, 611 std::memory_order_acquire)) { 612 // Update the tail pointer to the new offset. If the "else" clause did 613 // not exist, then this could be a simple Release_Store to set the new 614 // value but because it does, it's possible that other threads could add 615 // one or more nodes at the tail before reaching this point. We don't 616 // have to check the return value because it either operates correctly 617 // or the exact same operation has already been done (by the "else" 618 // clause) on some other thread. 619 shared_meta()->tailptr.compare_exchange_strong(tail, ref, 620 std::memory_order_release, 621 std::memory_order_relaxed); 622 return; 623 } else { 624 // In the unlikely case that a thread crashed or was killed between the 625 // update of "next" and the update of "tailptr", it is necessary to 626 // perform the operation that would have been done. There's no explicit 627 // check for crash/kill which means that this operation may also happen 628 // even when the other thread is in perfect working order which is what 629 // necessitates the CompareAndSwap above. 630 shared_meta()->tailptr.compare_exchange_strong(tail, next, 631 std::memory_order_acq_rel, 632 std::memory_order_acquire); 633 } 634 } 635 } 636 637 // The "corrupted" state is held both locally and globally (shared). The 638 // shared flag can't be trusted since a malicious actor could overwrite it. 639 // Because corruption can be detected during read-only operations such as 640 // iteration, this method may be called by other "const" methods. In this 641 // case, it's safe to discard the constness and modify the local flag and 642 // maybe even the shared flag if the underlying data isn't actually read-only. 643 void PersistentMemoryAllocator::SetCorrupt() const { 644 LOG(ERROR) << "Corruption detected in shared-memory segment."; 645 const_cast<std::atomic<bool>*>(&corrupt_)->store(true, 646 std::memory_order_relaxed); 647 if (!readonly_) { 648 SetFlag(const_cast<volatile std::atomic<uint32_t>*>(&shared_meta()->flags), 649 kFlagCorrupt); 650 } 651 } 652 653 bool PersistentMemoryAllocator::IsCorrupt() const { 654 if (corrupt_.load(std::memory_order_relaxed) || 655 CheckFlag(&shared_meta()->flags, kFlagCorrupt)) { 656 SetCorrupt(); // Make sure all indicators are set. 657 return true; 658 } 659 return false; 660 } 661 662 bool PersistentMemoryAllocator::IsFull() const { 663 return CheckFlag(&shared_meta()->flags, kFlagFull); 664 } 665 666 // Dereference a block |ref| and ensure that it's valid for the desired 667 // |type_id| and |size|. |special| indicates that we may try to access block 668 // headers not available to callers but still accessed by this module. By 669 // having internal dereferences go through this same function, the allocator 670 // is hardened against corruption. 671 const volatile PersistentMemoryAllocator::BlockHeader* 672 PersistentMemoryAllocator::GetBlock(Reference ref, uint32_t type_id, 673 uint32_t size, bool queue_ok, 674 bool free_ok) const { 675 // Validation of parameters. 676 if (ref % kAllocAlignment != 0) 677 return nullptr; 678 if (ref < (queue_ok ? kReferenceQueue : sizeof(SharedMetadata))) 679 return nullptr; 680 size += sizeof(BlockHeader); 681 if (ref + size > mem_size_) 682 return nullptr; 683 684 // Validation of referenced block-header. 685 if (!free_ok) { 686 uint32_t freeptr = std::min( 687 shared_meta()->freeptr.load(std::memory_order_relaxed), mem_size_); 688 if (ref + size > freeptr) 689 return nullptr; 690 const volatile BlockHeader* const block = 691 reinterpret_cast<volatile BlockHeader*>(mem_base_ + ref); 692 if (block->size < size) 693 return nullptr; 694 if (ref + block->size > freeptr) 695 return nullptr; 696 if (ref != kReferenceQueue && block->cookie != kBlockCookieAllocated) 697 return nullptr; 698 if (type_id != 0 && 699 block->type_id.load(std::memory_order_relaxed) != type_id) { 700 return nullptr; 701 } 702 } 703 704 // Return pointer to block data. 705 return reinterpret_cast<const volatile BlockHeader*>(mem_base_ + ref); 706 } 707 708 const volatile void* PersistentMemoryAllocator::GetBlockData( 709 Reference ref, 710 uint32_t type_id, 711 uint32_t size) const { 712 DCHECK(size > 0); 713 const volatile BlockHeader* block = 714 GetBlock(ref, type_id, size, false, false); 715 if (!block) 716 return nullptr; 717 return reinterpret_cast<const volatile char*>(block) + sizeof(BlockHeader); 718 } 719 720 void PersistentMemoryAllocator::UpdateTrackingHistograms() { 721 DCHECK(!readonly_); 722 if (used_histogram_) { 723 MemoryInfo meminfo; 724 GetMemoryInfo(&meminfo); 725 HistogramBase::Sample used_percent = static_cast<HistogramBase::Sample>( 726 ((meminfo.total - meminfo.free) * 100ULL / meminfo.total)); 727 used_histogram_->Add(used_percent); 728 } 729 } 730 731 732 //----- LocalPersistentMemoryAllocator ----------------------------------------- 733 734 LocalPersistentMemoryAllocator::LocalPersistentMemoryAllocator( 735 size_t size, 736 uint64_t id, 737 base::StringPiece name) 738 : PersistentMemoryAllocator(AllocateLocalMemory(size), 739 size, 0, id, name, false) {} 740 741 LocalPersistentMemoryAllocator::~LocalPersistentMemoryAllocator() { 742 DeallocateLocalMemory(const_cast<char*>(mem_base_), mem_size_); 743 } 744 745 // static 746 void* LocalPersistentMemoryAllocator::AllocateLocalMemory(size_t size) { 747 #if defined(OS_WIN) 748 void* address = 749 ::VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 750 DPCHECK(address); 751 return address; 752 #elif defined(OS_POSIX) 753 // MAP_ANON is deprecated on Linux but MAP_ANONYMOUS is not universal on Mac. 754 // MAP_SHARED is not available on Linux <2.4 but required on Mac. 755 void* address = ::mmap(nullptr, size, PROT_READ | PROT_WRITE, 756 MAP_ANON | MAP_SHARED, -1, 0); 757 DPCHECK(MAP_FAILED != address); 758 return address; 759 #else 760 #error This architecture is not (yet) supported. 761 #endif 762 } 763 764 // static 765 void LocalPersistentMemoryAllocator::DeallocateLocalMemory(void* memory, 766 size_t size) { 767 #if defined(OS_WIN) 768 BOOL success = ::VirtualFree(memory, 0, MEM_DECOMMIT); 769 DPCHECK(success); 770 #elif defined(OS_POSIX) 771 int result = ::munmap(memory, size); 772 DPCHECK(0 == result); 773 #else 774 #error This architecture is not (yet) supported. 775 #endif 776 } 777 778 779 //----- SharedPersistentMemoryAllocator ---------------------------------------- 780 781 SharedPersistentMemoryAllocator::SharedPersistentMemoryAllocator( 782 std::unique_ptr<SharedMemory> memory, 783 uint64_t id, 784 base::StringPiece name, 785 bool read_only) 786 : PersistentMemoryAllocator(static_cast<uint8_t*>(memory->memory()), 787 memory->mapped_size(), 788 0, 789 id, 790 name, 791 read_only), 792 shared_memory_(std::move(memory)) {} 793 794 SharedPersistentMemoryAllocator::~SharedPersistentMemoryAllocator() {} 795 796 // static 797 bool SharedPersistentMemoryAllocator::IsSharedMemoryAcceptable( 798 const SharedMemory& memory) { 799 return IsMemoryAcceptable(memory.memory(), memory.mapped_size(), 0, false); 800 } 801 802 803 #if !defined(OS_NACL) 804 //----- FilePersistentMemoryAllocator ------------------------------------------ 805 806 FilePersistentMemoryAllocator::FilePersistentMemoryAllocator( 807 std::unique_ptr<MemoryMappedFile> file, 808 size_t max_size, 809 uint64_t id, 810 base::StringPiece name, 811 bool read_only) 812 : PersistentMemoryAllocator(const_cast<uint8_t*>(file->data()), 813 max_size != 0 ? max_size : file->length(), 814 0, 815 id, 816 name, 817 read_only), 818 mapped_file_(std::move(file)) {} 819 820 FilePersistentMemoryAllocator::~FilePersistentMemoryAllocator() {} 821 822 // static 823 bool FilePersistentMemoryAllocator::IsFileAcceptable( 824 const MemoryMappedFile& file, 825 bool read_only) { 826 return IsMemoryAcceptable(file.data(), file.length(), 0, read_only); 827 } 828 #endif // !defined(OS_NACL) 829 830 } // namespace base 831