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