early_zone_registration_mac.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. // Copyright 2021 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. #include "base/allocator/early_zone_registration_mac.h"
  5. #include <mach/mach.h>
  6. #include <malloc/malloc.h>
  7. #include "base/allocator/buildflags.h"
  8. // BASE_EXPORT tends to be defined as soon as anything from //base is included.
  9. #if defined(BASE_EXPORT)
  10. #error "This file cannot depend on //base"
  11. #endif
  12. namespace partition_alloc {
  13. #if !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
  14. void EarlyMallocZoneRegistration() {}
  15. void AllowDoublePartitionAllocZoneRegistration() {}
  16. #else
  17. extern "C" {
  18. // abort_report_np() records the message in a special section that both the
  19. // system CrashReporter and Crashpad collect in crash reports. See also in
  20. // chrome_exe_main_mac.cc.
  21. void abort_report_np(const char* fmt, ...);
  22. }
  23. namespace {
  24. malloc_zone_t* GetDefaultMallocZone() {
  25. // malloc_default_zone() does not return... the default zone, but the
  26. // initial one. The default one is the first element of the default zone
  27. // array.
  28. unsigned int zone_count = 0;
  29. vm_address_t* zones = nullptr;
  30. kern_return_t result =
  31. malloc_get_all_zones(mach_task_self(), nullptr, &zones, &zone_count);
  32. if (result != KERN_SUCCESS)
  33. abort_report_np("Cannot enumerate malloc() zones");
  34. return reinterpret_cast<malloc_zone_t*>(zones[0]);
  35. }
  36. } // namespace
  37. void EarlyMallocZoneRegistration() {
  38. // Must have static storage duration, as raw pointers are passed to
  39. // libsystem_malloc.
  40. static malloc_zone_t g_delegating_zone;
  41. static malloc_introspection_t g_delegating_zone_introspect;
  42. static malloc_zone_t* g_default_zone;
  43. // Make sure that the default zone is instantiated.
  44. malloc_zone_t* purgeable_zone = malloc_default_purgeable_zone();
  45. g_default_zone = GetDefaultMallocZone();
  46. // The delegating zone:
  47. // - Forwards all allocations to the existing default zone
  48. // - Does *not* claim to own any memory, meaning that it will always be
  49. // skipped in free() in libsystem_malloc.dylib.
  50. //
  51. // This is a temporary zone, until it gets replaced by PartitionAlloc, inside
  52. // the main library. Since the main library depends on many external
  53. // libraries, we cannot install PartitionAlloc as the default zone without
  54. // concurrency issues.
  55. //
  56. // Instead, what we do is here, while the process is single-threaded:
  57. // - Register the delegating zone as the default one.
  58. // - Set the original (libsystem_malloc's) one as the second zone
  59. //
  60. // Later, when PartitionAlloc initializes, we replace the default (delegating)
  61. // zone with ours. The end state is:
  62. // 1. PartitionAlloc zone
  63. // 2. libsystem_malloc zone
  64. // Set up of the delegating zone. Note that it doesn't just forward calls to
  65. // the default zone. This is because the system zone's malloc_zone_t pointer
  66. // actually points to a larger struct, containing allocator metadata. So if we
  67. // pass as the first parameter the "simple" delegating zone pointer, then we
  68. // immediately crash inside the system zone functions. So we need to replace
  69. // the zone pointer as well.
  70. //
  71. // Calls fall into 4 categories:
  72. // - Allocation calls: forwarded to the real system zone
  73. // - "Is this pointer yours" calls: always answer no
  74. // - free(): Should never be called, but is in practice, see comments below.
  75. // - Diagnostics and debugging: these are typically called for every
  76. // zone. They are no-ops for us, as we don't want to double-count, or lock
  77. // the data structures of the real zone twice.
  78. // Allocation: Forward to the real zone.
  79. g_delegating_zone.malloc = [](malloc_zone_t* zone, size_t size) {
  80. return g_default_zone->malloc(g_default_zone, size);
  81. };
  82. g_delegating_zone.calloc = [](malloc_zone_t* zone, size_t num_items,
  83. size_t size) {
  84. return g_default_zone->calloc(g_default_zone, num_items, size);
  85. };
  86. g_delegating_zone.valloc = [](malloc_zone_t* zone, size_t size) {
  87. return g_default_zone->valloc(g_default_zone, size);
  88. };
  89. g_delegating_zone.realloc = [](malloc_zone_t* zone, void* ptr, size_t size) {
  90. return g_default_zone->realloc(g_default_zone, ptr, size);
  91. };
  92. g_delegating_zone.batch_malloc = [](malloc_zone_t* zone, size_t size,
  93. void** results, unsigned num_requested) {
  94. return g_default_zone->batch_malloc(g_default_zone, size, results,
  95. num_requested);
  96. };
  97. g_delegating_zone.memalign = [](malloc_zone_t* zone, size_t alignment,
  98. size_t size) {
  99. return g_default_zone->memalign(g_default_zone, alignment, size);
  100. };
  101. // Does ptr belong to this zone? Return value is != 0 if so.
  102. g_delegating_zone.size = [](malloc_zone_t* zone, const void* ptr) -> size_t {
  103. return 0;
  104. };
  105. // Free functions.
  106. // The normal path for freeing memory is:
  107. // 1. Try all zones in order, call zone->size(ptr)
  108. // 2. If zone->size(ptr) != 0, call zone->free(ptr) (or free_definite_size)
  109. // 3. If no zone matches, crash.
  110. //
  111. // Since this zone always returns 0 in size() (see above), then zone->free()
  112. // should never be called. Unfortunately, this is not the case, as some places
  113. // in CoreFoundation call malloc_zone_free(zone, ptr) directly. So rather than
  114. // crashing, forward the call. It's the caller's responsibility to use the
  115. // same zone for free() as for the allocation (this is in the contract of
  116. // malloc_zone_free()).
  117. //
  118. // However, note that the sequence of calls size() -> free() is not possible
  119. // for this zone, as size() always returns 0.
  120. g_delegating_zone.free = [](malloc_zone_t* zone, void* ptr) {
  121. return g_default_zone->free(g_default_zone, ptr);
  122. };
  123. g_delegating_zone.free_definite_size = [](malloc_zone_t* zone, void* ptr,
  124. size_t size) {
  125. return g_default_zone->free_definite_size(g_default_zone, ptr, size);
  126. };
  127. g_delegating_zone.batch_free = [](malloc_zone_t* zone, void** to_be_freed,
  128. unsigned num_to_be_freed) {
  129. return g_default_zone->batch_free(g_default_zone, to_be_freed,
  130. num_to_be_freed);
  131. };
  132. // Diagnostics and debugging.
  133. //
  134. // Do nothing to reduce memory footprint, the real
  135. // zone will do it.
  136. g_delegating_zone.pressure_relief = [](malloc_zone_t* zone,
  137. size_t goal) -> size_t { return 0; };
  138. // Introspection calls are not all optional, for instance locking and
  139. // unlocking before/after fork() is not optional.
  140. //
  141. // Nothing to enumerate.
  142. g_delegating_zone_introspect.enumerator =
  143. [](task_t task, void*, unsigned type_mask, vm_address_t zone_address,
  144. memory_reader_t reader,
  145. vm_range_recorder_t recorder) -> kern_return_t {
  146. return KERN_SUCCESS;
  147. };
  148. // Need to provide a real implementation, it is used for e.g. array sizing.
  149. g_delegating_zone_introspect.good_size = [](malloc_zone_t* zone,
  150. size_t size) {
  151. return g_default_zone->introspect->good_size(g_default_zone, size);
  152. };
  153. // Nothing to do.
  154. g_delegating_zone_introspect.check = [](malloc_zone_t* zone) -> boolean_t {
  155. return true;
  156. };
  157. g_delegating_zone_introspect.print = [](malloc_zone_t* zone,
  158. boolean_t verbose) {};
  159. g_delegating_zone_introspect.log = [](malloc_zone_t*, void*) {};
  160. // Do not forward the lock / unlock calls. Since the default zone is still
  161. // there, we should not lock here, as it would lock the zone twice (all
  162. // zones are locked before fork().). Rather, do nothing, since this fake
  163. // zone does not need any locking.
  164. g_delegating_zone_introspect.force_lock = [](malloc_zone_t* zone) {};
  165. g_delegating_zone_introspect.force_unlock = [](malloc_zone_t* zone) {};
  166. g_delegating_zone_introspect.reinit_lock = [](malloc_zone_t* zone) {};
  167. // No stats.
  168. g_delegating_zone_introspect.statistics = [](malloc_zone_t* zone,
  169. malloc_statistics_t* stats) {};
  170. // We are not locked.
  171. g_delegating_zone_introspect.zone_locked =
  172. [](malloc_zone_t* zone) -> boolean_t { return false; };
  173. // Don't support discharge checking.
  174. g_delegating_zone_introspect.enable_discharge_checking =
  175. [](malloc_zone_t* zone) -> boolean_t { return false; };
  176. g_delegating_zone_introspect.disable_discharge_checking =
  177. [](malloc_zone_t* zone) {};
  178. g_delegating_zone_introspect.discharge = [](malloc_zone_t* zone,
  179. void* memory) {};
  180. // Could use something lower to support fewer functions, but this is
  181. // consistent with the real zone installed by PartitionAlloc.
  182. g_delegating_zone.version = kZoneVersion;
  183. g_delegating_zone.introspect = &g_delegating_zone_introspect;
  184. // This name is used in PartitionAlloc's initialization to determine whether
  185. // it should replace the delegating zone.
  186. g_delegating_zone.zone_name = kDelegatingZoneName;
  187. // Register puts the new zone at the end, unregister swaps the new zone with
  188. // the last one.
  189. // The zone array is, after these lines, in order:
  190. // 1. |g_default_zone|...|g_delegating_zone|
  191. // 2. |g_delegating_zone|...| (no more default)
  192. // 3. |g_delegating_zone|...|g_default_zone|
  193. malloc_zone_register(&g_delegating_zone);
  194. malloc_zone_unregister(g_default_zone);
  195. malloc_zone_register(g_default_zone);
  196. // Make sure that the purgeable zone is after the default one.
  197. // Will make g_default_zone take the purgeable zone spot
  198. malloc_zone_unregister(purgeable_zone);
  199. // Add back the purgeable zone as the last one.
  200. malloc_zone_register(purgeable_zone);
  201. // Final configuration:
  202. // |g_delegating_zone|...|g_default_zone|purgeable_zone|
  203. // Sanity check.
  204. if (GetDefaultMallocZone() != &g_delegating_zone)
  205. abort_report_np("Failed to install the delegating zone as default.");
  206. }
  207. void AllowDoublePartitionAllocZoneRegistration() {
  208. unsigned int zone_count = 0;
  209. vm_address_t* zones = nullptr;
  210. kern_return_t result =
  211. malloc_get_all_zones(mach_task_self(), nullptr, &zones, &zone_count);
  212. if (result != KERN_SUCCESS)
  213. abort_report_np("Cannot enumerate malloc() zones");
  214. // If PartitionAlloc is one of the zones, *change* its name so that
  215. // registration can happen multiple times. This works because zone
  216. // registration only keeps a pointer to the struct, it does not copy the data.
  217. for (unsigned int i = 0; i < zone_count; i++) {
  218. malloc_zone_t* zone = reinterpret_cast<malloc_zone_t*>(zones[i]);
  219. if (zone->zone_name &&
  220. strcmp(zone->zone_name, kPartitionAllocZoneName) == 0) {
  221. zone->zone_name = "RenamedPartitionAlloc";
  222. break;
  223. }
  224. }
  225. }
  226. #endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
  227. } // namespace partition_alloc