blob: 6d7b5c96255ac7e148966973d2e5f0db703fea77 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "partition_alloc/shim/malloc_zone_functions_apple.h"
#include <atomic>
#include <type_traits>
#include "partition_alloc/partition_alloc_base/check.h"
#include "partition_alloc/partition_lock.h"
namespace allocator_shim {
MallocZoneFunctions g_malloc_zones[kMaxZoneCount];
static_assert(std::is_trivial_v<MallocZoneFunctions> &&
std::is_standard_layout_v<MallocZoneFunctions>,
"MallocZoneFunctions must be POD");
void StoreZoneFunctions(const ChromeMallocZone* zone,
MallocZoneFunctions* functions) {
memset(functions, 0, sizeof(MallocZoneFunctions));
functions->malloc = zone->malloc;
functions->calloc = zone->calloc;
functions->valloc = zone->valloc;
functions->free = zone->free;
functions->realloc = zone->realloc;
functions->size = zone->size;
functions->good_size = zone->introspect->good_size;
PA_BASE_CHECK(functions->malloc && functions->calloc && functions->valloc &&
functions->free && functions->realloc && functions->size &&
functions->good_size);
// These functions might be nullptr.
functions->batch_malloc = zone->batch_malloc;
functions->batch_free = zone->batch_free;
if (zone->version >= 5) {
// Not all custom malloc zones have a memalign.
functions->memalign = zone->memalign;
}
if (zone->version >= 6) {
// This may be nullptr.
functions->free_definite_size = zone->free_definite_size;
}
if (zone->version >= 10) {
functions->claimed_address = zone->claimed_address;
}
if (zone->version >= 13) {
functions->try_free_default = zone->try_free_default;
}
// Note that zone version 8 introduced a pressure relief callback, and version
// 10 introduced a claimed address callback, but neither are allocation or
// deallocation callbacks and so aren't important to intercept.
functions->context = zone;
}
namespace {
// All modifications to g_malloc_zones are gated behind this lock.
// Dispatch to a malloc zone does not need to acquire this lock.
partition_alloc::internal::Lock& GetLock() {
static partition_alloc::internal::Lock s_lock;
return s_lock;
}
void EnsureMallocZonesInitializedLocked() {
GetLock().AssertAcquired();
}
int g_zone_count = 0;
bool IsMallocZoneAlreadyStoredLocked(ChromeMallocZone* zone) {
EnsureMallocZonesInitializedLocked();
for (int i = 0; i < g_zone_count; ++i) {
if (g_malloc_zones[i].context == reinterpret_cast<void*>(zone)) {
return true;
}
}
return false;
}
} // namespace
bool StoreMallocZone(ChromeMallocZone* zone) {
partition_alloc::internal::ScopedGuard guard(GetLock());
if (IsMallocZoneAlreadyStoredLocked(zone)) {
return false;
}
if (g_zone_count == kMaxZoneCount) {
return false;
}
StoreZoneFunctions(zone, &g_malloc_zones[g_zone_count]);
++g_zone_count;
// No other thread can possibly see these stores at this point. The code that
// reads these values is triggered after this function returns. so we want to
// guarantee that they are committed at this stage"
std::atomic_thread_fence(std::memory_order_seq_cst);
return true;
}
bool IsMallocZoneAlreadyStored(ChromeMallocZone* zone) {
partition_alloc::internal::ScopedGuard guard(GetLock());
return IsMallocZoneAlreadyStoredLocked(zone);
}
bool DoesMallocZoneNeedReplacing(ChromeMallocZone* zone,
const MallocZoneFunctions* functions) {
return IsMallocZoneAlreadyStored(zone) && zone->malloc != functions->malloc;
}
int GetMallocZoneCountForTesting() {
partition_alloc::internal::ScopedGuard guard(GetLock());
return g_zone_count;
}
void ClearAllMallocZonesForTesting() {
partition_alloc::internal::ScopedGuard guard(GetLock());
memset(g_malloc_zones, 0, kMaxZoneCount * sizeof(MallocZoneFunctions));
g_zone_count = 0;
}
} // namespace allocator_shim