1 // Copyright 2017 The Chromium Authors
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 "partition_alloc/shim/malloc_zone_functions_apple.h"
6
7 #include <atomic>
8 #include <type_traits>
9
10 #include "partition_alloc/partition_alloc_base/check.h"
11 #include "partition_alloc/partition_lock.h"
12
13 namespace allocator_shim {
14
15 MallocZoneFunctions g_malloc_zones[kMaxZoneCount];
16 static_assert(std::is_trivial_v<MallocZoneFunctions> &&
17 std::is_standard_layout_v<MallocZoneFunctions>,
18 "MallocZoneFunctions must be POD");
19
StoreZoneFunctions(const ChromeMallocZone * zone,MallocZoneFunctions * functions)20 void StoreZoneFunctions(const ChromeMallocZone* zone,
21 MallocZoneFunctions* functions) {
22 memset(functions, 0, sizeof(MallocZoneFunctions));
23 functions->malloc = zone->malloc;
24 functions->calloc = zone->calloc;
25 functions->valloc = zone->valloc;
26 functions->free = zone->free;
27 functions->realloc = zone->realloc;
28 functions->size = zone->size;
29 functions->good_size = zone->introspect->good_size;
30 PA_BASE_CHECK(functions->malloc && functions->calloc && functions->valloc &&
31 functions->free && functions->realloc && functions->size &&
32 functions->good_size);
33
34 // These functions might be nullptr.
35 functions->batch_malloc = zone->batch_malloc;
36 functions->batch_free = zone->batch_free;
37
38 if (zone->version >= 5) {
39 // Not all custom malloc zones have a memalign.
40 functions->memalign = zone->memalign;
41 }
42 if (zone->version >= 6) {
43 // This may be nullptr.
44 functions->free_definite_size = zone->free_definite_size;
45 }
46 if (zone->version >= 10) {
47 functions->claimed_address = zone->claimed_address;
48 }
49 if (zone->version >= 13) {
50 functions->try_free_default = zone->try_free_default;
51 }
52
53 // Note that zone version 8 introduced a pressure relief callback, and version
54 // 10 introduced a claimed address callback, but neither are allocation or
55 // deallocation callbacks and so aren't important to intercept.
56
57 functions->context = zone;
58 }
59
60 namespace {
61
62 // All modifications to g_malloc_zones are gated behind this lock.
63 // Dispatch to a malloc zone does not need to acquire this lock.
GetLock()64 partition_alloc::internal::Lock& GetLock() {
65 static partition_alloc::internal::Lock s_lock;
66 return s_lock;
67 }
68
EnsureMallocZonesInitializedLocked()69 void EnsureMallocZonesInitializedLocked() {
70 GetLock().AssertAcquired();
71 }
72
73 int g_zone_count = 0;
74
IsMallocZoneAlreadyStoredLocked(ChromeMallocZone * zone)75 bool IsMallocZoneAlreadyStoredLocked(ChromeMallocZone* zone) {
76 EnsureMallocZonesInitializedLocked();
77 for (int i = 0; i < g_zone_count; ++i) {
78 if (g_malloc_zones[i].context == reinterpret_cast<void*>(zone)) {
79 return true;
80 }
81 }
82 return false;
83 }
84
85 } // namespace
86
StoreMallocZone(ChromeMallocZone * zone)87 bool StoreMallocZone(ChromeMallocZone* zone) {
88 partition_alloc::internal::ScopedGuard guard(GetLock());
89 if (IsMallocZoneAlreadyStoredLocked(zone)) {
90 return false;
91 }
92
93 if (g_zone_count == kMaxZoneCount) {
94 return false;
95 }
96
97 StoreZoneFunctions(zone, &g_malloc_zones[g_zone_count]);
98 ++g_zone_count;
99
100 // No other thread can possibly see these stores at this point. The code that
101 // reads these values is triggered after this function returns. so we want to
102 // guarantee that they are committed at this stage"
103 std::atomic_thread_fence(std::memory_order_seq_cst);
104 return true;
105 }
106
IsMallocZoneAlreadyStored(ChromeMallocZone * zone)107 bool IsMallocZoneAlreadyStored(ChromeMallocZone* zone) {
108 partition_alloc::internal::ScopedGuard guard(GetLock());
109 return IsMallocZoneAlreadyStoredLocked(zone);
110 }
111
DoesMallocZoneNeedReplacing(ChromeMallocZone * zone,const MallocZoneFunctions * functions)112 bool DoesMallocZoneNeedReplacing(ChromeMallocZone* zone,
113 const MallocZoneFunctions* functions) {
114 return IsMallocZoneAlreadyStored(zone) && zone->malloc != functions->malloc;
115 }
116
GetMallocZoneCountForTesting()117 int GetMallocZoneCountForTesting() {
118 partition_alloc::internal::ScopedGuard guard(GetLock());
119 return g_zone_count;
120 }
121
ClearAllMallocZonesForTesting()122 void ClearAllMallocZonesForTesting() {
123 partition_alloc::internal::ScopedGuard guard(GetLock());
124 memset(g_malloc_zones, 0, kMaxZoneCount * sizeof(MallocZoneFunctions));
125 g_zone_count = 0;
126 }
127
128 } // namespace allocator_shim
129