#include "partition_alloc/partition_root.h"
#include <cstdint>
#include "partition_alloc/build_config.h"
#include "partition_alloc/buildflags.h"
#include "partition_alloc/freeslot_bitmap.h"
#include "partition_alloc/in_slot_metadata.h"
#include "partition_alloc/oom.h"
#include "partition_alloc/page_allocator.h"
#include "partition_alloc/partition_address_space.h"
#include "partition_alloc/partition_alloc-inl.h"
#include "partition_alloc/partition_alloc_base/bits.h"
#include "partition_alloc/partition_alloc_base/compiler_specific.h"
#include "partition_alloc/partition_alloc_base/component_export.h"
#include "partition_alloc/partition_alloc_base/thread_annotations.h"
#include "partition_alloc/partition_alloc_check.h"
#include "partition_alloc/partition_alloc_config.h"
#include "partition_alloc/partition_alloc_constants.h"
#include "partition_alloc/partition_bucket.h"
#include "partition_alloc/partition_cookie.h"
#include "partition_alloc/partition_oom.h"
#include "partition_alloc/partition_page.h"
#include "partition_alloc/reservation_offset_table.h"
#include "partition_alloc/tagging.h"
#include "partition_alloc/thread_isolation/thread_isolation.h"
#if PA_BUILDFLAG(IS_MAC)
#include "partition_alloc/partition_alloc_base/mac/mac_util.h"
#endif
#if !PA_BUILDFLAG(HAS_64_BIT_POINTERS)
#include "partition_alloc/address_pool_manager_bitmap.h"
#endif
#if PA_BUILDFLAG(IS_WIN)
#include <windows.h>
#include "wow64apiset.h"
#endif
#if PA_BUILDFLAG(IS_LINUX) || PA_BUILDFLAG(IS_CHROMEOS)
#include <pthread.h>
#endif
namespace partition_alloc::internal {
#if PA_BUILDFLAG(RECORD_ALLOC_INFO)
AllocInfo g_allocs = {};
void RecordAllocOrFree(uintptr_t addr, size_t size) {
g_allocs.allocs[g_allocs.index.fetch_add(1, std::memory_order_relaxed) %
kAllocInfoSize] = {addr, size};
}
#endif
#if PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
PtrPosWithinAlloc IsPtrWithinSameAlloc(uintptr_t orig_address,
uintptr_t test_address,
size_t type_size) { … }
#endif
}
namespace partition_alloc {
#if PA_CONFIG(USE_PARTITION_ROOT_ENUMERATOR)
namespace {
internal::Lock g_root_enumerator_lock;
}
internal::Lock& PartitionRoot::GetEnumeratorLock() { … }
namespace internal {
class PartitionRootEnumerator { … };
}
#endif
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
namespace {
#if PA_CONFIG(HAS_ATFORK_HANDLER)
void LockRoot(PartitionRoot* root, bool) PA_NO_THREAD_SAFETY_ANALYSIS { … }
void BeforeForkInParent() PA_NO_THREAD_SAFETY_ANALYSIS { … }
template <typename T>
void UnlockOrReinit(T& lock, bool in_child) PA_NO_THREAD_SAFETY_ANALYSIS { … }
void UnlockOrReinitRoot(PartitionRoot* root,
bool in_child) PA_NO_THREAD_SAFETY_ANALYSIS { … }
void ReleaseLocks(bool in_child) PA_NO_THREAD_SAFETY_ANALYSIS { … }
void AfterForkInParent() { … }
void AfterForkInChild() { … }
#endif
std::atomic<bool> g_global_init_called;
void PartitionAllocMallocInitOnce() { … }
}
#if PA_BUILDFLAG(IS_APPLE)
void PartitionAllocMallocHookOnBeforeForkInParent() {
BeforeForkInParent();
}
void PartitionAllocMallocHookOnAfterForkInParent() {
AfterForkInParent();
}
void PartitionAllocMallocHookOnAfterForkInChild() {
AfterForkInChild();
}
#endif
#endif
#if PA_CONFIG(ENABLE_SHADOW_METADATA)
namespace {
void MakeSuperPageExtentEntriesShared(PartitionRoot* root,
internal::PoolHandleMask mask)
PA_NO_THREAD_SAFETY_ANALYSIS {
PA_DCHECK(root);
switch (root->ChoosePool()) {
case internal::kRegularPoolHandle:
if (!ContainsFlags(mask, internal::PoolHandleMask::kRegular)) {
return;
}
root->settings.shadow_pool_offset_ =
internal::PartitionAddressSpace::RegularPoolShadowOffset();
break;
case internal::kBRPPoolHandle:
if (!ContainsFlags(mask, internal::PoolHandleMask::kBRP)) {
return;
}
root->settings.shadow_pool_offset_ =
internal::PartitionAddressSpace::BRPPoolShadowOffset();
break;
case internal::kConfigurablePoolHandle:
if (!ContainsFlags(mask, internal::PoolHandleMask::kConfigurable)) {
return;
}
root->settings.shadow_pool_offset_ =
internal::PartitionAddressSpace::ConfigurablePoolShadowOffset();
break;
default:
return;
}
for (const internal::PartitionSuperPageExtentEntry<MetadataKind::kReadOnly>*
extent = root->first_extent;
extent != nullptr; extent = extent->next) {
uintptr_t super_page = SuperPagesBeginFromExtent(extent);
for (size_t i = 0; i < extent->number_of_consecutive_super_pages; ++i) {
internal::PartitionAddressSpace::MapMetadata(super_page,
true);
super_page += kSuperPageSize;
}
PA_DCHECK(extent->root == root);
}
for (const internal::PartitionDirectMapExtent<MetadataKind::kReadOnly>*
extent = root->direct_map_list;
extent != nullptr; extent = extent->next_extent) {
internal::PartitionAddressSpace::MapMetadata(
reinterpret_cast<uintptr_t>(extent) & internal::kSuperPageBaseMask,
true);
}
}
}
#endif
namespace internal {
namespace {
constexpr size_t kMaxPurgeableSlotsPerSystemPage = …;
constexpr size_t kConservativeMaxPurgeableSlotsPerSystemPage = …;
PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t
MinPurgeableSlotSize() { … }
PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t
MinConservativePurgeableSlotSize() { … }
}
PA_NOPROFILE
static size_t PartitionPurgeSlotSpan(PartitionRoot* root,
internal::SlotSpanMetadata* slot_span,
bool accounting_only)
PA_EXCLUSIVE_LOCKS_REQUIRED(internal::PartitionRootLock(root)) { … }
PA_NOPROFILE
static void PartitionPurgeBucket(PartitionRoot* root,
internal::PartitionBucket* bucket)
PA_EXCLUSIVE_LOCKS_REQUIRED(internal::PartitionRootLock(root)) { … }
static void PartitionDumpSlotSpanStats(PartitionBucketMemoryStats* stats_out,
PartitionRoot* root,
internal::SlotSpanMetadata* slot_span)
PA_EXCLUSIVE_LOCKS_REQUIRED(internal::PartitionRootLock(root)) { … }
static void PartitionDumpBucketStats(PartitionBucketMemoryStats* stats_out,
PartitionRoot* root,
const internal::PartitionBucket* bucket)
PA_EXCLUSIVE_LOCKS_REQUIRED(internal::PartitionRootLock(root)) { … }
#if PA_BUILDFLAG(DCHECKS_ARE_ON)
void DCheckIfManagedByPartitionAllocBRPPool(uintptr_t address) { … }
#endif
#if PA_BUILDFLAG(ENABLE_THREAD_ISOLATION)
void PartitionAllocThreadIsolationInit(ThreadIsolationOption thread_isolation) { … }
#endif
}
[[noreturn]] PA_NOINLINE void PartitionRoot::OutOfMemory(size_t size) { … }
void PartitionRoot::DecommitEmptySlotSpans() { … }
void PartitionRoot::DecommitEmptySlotSpansForTesting() { … }
void PartitionRoot::DestructForTesting()
PA_EXCLUSIVE_LOCKS_REQUIRED(internal::PartitionRootLock(this)) { … }
#if PA_CONFIG(MAYBE_ENABLE_MAC11_MALLOC_SIZE_HACK)
void PartitionRoot::InitMac11MallocSizeHackUsableSize() {
settings.mac11_malloc_size_hack_enabled_ = true;
PA_DCHECK(settings.in_slot_metadata_size);
settings.mac11_malloc_size_hack_usable_size_ =
48 - settings.in_slot_metadata_size;
}
void PartitionRoot::EnableMac11MallocSizeHackForTesting() {
InitMac11MallocSizeHackUsableSize();
}
void PartitionRoot::EnableMac11MallocSizeHackIfNeeded() {
PA_DCHECK(settings.brp_enabled_);
if (internal::base::mac::MacOSMajorVersion() == 11) {
InitMac11MallocSizeHackUsableSize();
}
}
#endif
#if PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) && \
!PA_BUILDFLAG(HAS_64_BIT_POINTERS)
namespace {
std::atomic<bool> g_reserve_brp_guard_region_called;
void ReserveBackupRefPtrGuardRegionIfNeeded() {
bool expected = false;
if (!g_reserve_brp_guard_region_called.compare_exchange_strong(expected,
true)) {
return;
}
size_t alignment = internal::PageAllocationGranularity();
uintptr_t requested_address;
memset(&requested_address, internal::kQuarantinedByte,
sizeof(requested_address));
requested_address = RoundDownToPageAllocationGranularity(requested_address);
for (size_t i = 0; i < 4; ++i) {
[[maybe_unused]] uintptr_t allocated_address =
AllocPages(requested_address, alignment, alignment,
PageAccessibilityConfiguration(
PageAccessibilityConfiguration::kInaccessible),
PageTag::kPartitionAlloc);
requested_address += alignment;
}
}
}
#endif
void PartitionRoot::Init(PartitionOptions opts) { … }
PartitionRoot::Settings::Settings() = default;
PartitionRoot::PartitionRoot() : … { … }
PartitionRoot::PartitionRoot(PartitionOptions opts)
: … { … }
PartitionRoot::~PartitionRoot() { … }
void PartitionRoot::EnableThreadCacheIfSupported() { … }
bool PartitionRoot::TryReallocInPlaceForDirectMap(
internal::SlotSpanMetadata* slot_span,
size_t requested_size) { … }
bool PartitionRoot::TryReallocInPlaceForNormalBuckets(
void* object,
SlotSpanMetadata* slot_span,
size_t new_size) { … }
void PartitionRoot::PurgeMemory(int flags) { … }
void PartitionRoot::ShrinkEmptySlotSpansRing(size_t limit) { … }
void PartitionRoot::DumpStats(const char* partition_name,
bool is_light_dump,
PartitionStatsDumper* dumper) { … }
void PartitionRoot::DeleteForTesting(PartitionRoot* partition_root) { … }
void PartitionRoot::ResetForTesting(bool allow_leaks) { … }
void PartitionRoot::ResetBookkeepingForTesting() { … }
void PartitionRoot::SetGlobalEmptySlotSpanRingIndexForTesting(int16_t index) { … }
ThreadCache* PartitionRoot::MaybeInitThreadCache() { … }
void PartitionRoot::SetStraightenLargerSlotSpanFreeListsMode(
StraightenLargerSlotSpanFreeListsMode new_value) { … }
void PartitionRoot::SetSortSmallerSlotSpanFreeListsEnabled(bool new_value) { … }
void PartitionRoot::SetSortActiveSlotSpansEnabled(bool new_value) { … }
#if PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
PA_NOINLINE void PartitionRoot::QuarantineForBrp(
const SlotSpanMetadata* slot_span,
void* object) { … }
#endif
#if PA_CONFIG(ENABLE_SHADOW_METADATA)
void PartitionRoot::EnableShadowMetadata(internal::PoolHandleMask mask) {
internal::ScopedGuard guard(g_root_enumerator_lock);
internal::PartitionRootEnumerator::Instance().Enumerate(
LockRoot, false,
internal::PartitionRootEnumerator::EnumerateOrder::kNormal);
{
internal::ScopedGuard thread_cache_guard(ThreadCacheRegistry::GetLock());
internal::PartitionAddressSpace::InitShadowMetadata(mask);
internal::PartitionRootEnumerator::Instance().Enumerate(
MakeSuperPageExtentEntriesShared, mask,
internal::PartitionRootEnumerator::EnumerateOrder::kNormal);
}
internal::PartitionRootEnumerator::Instance().Enumerate(
UnlockOrReinitRoot, false,
internal::PartitionRootEnumerator::EnumerateOrder::kReverse);
}
#endif
#define EXPORT_TEMPLATE …
EXPORT_TEMPLATE void* PartitionRoot::Alloc<AllocFlags::kNone>(size_t,
const char*);
EXPORT_TEMPLATE void* PartitionRoot::Alloc<AllocFlags::kReturnNull>(
size_t,
const char*);
EXPORT_TEMPLATE void*
PartitionRoot::Realloc<AllocFlags::kNone, FreeFlags::kNone>(void*,
size_t,
const char*);
EXPORT_TEMPLATE void*
PartitionRoot::Realloc<AllocFlags::kReturnNull, FreeFlags::kNone>(void*,
size_t,
const char*);
EXPORT_TEMPLATE void* PartitionRoot::AlignedAlloc<AllocFlags::kNone>(size_t,
size_t);
#undef EXPORT_TEMPLATE
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Winvalid-offsetof"
#endif
static_assert …;
static_assert …;
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
}