sanitizer_malloc_mac.inc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. //===-- sanitizer_malloc_mac.inc --------------------------------*- C++ -*-===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // This file contains Mac-specific malloc interceptors and a custom zone
  10. // implementation, which together replace the system allocator.
  11. //
  12. //===----------------------------------------------------------------------===//
  13. #include "sanitizer_common/sanitizer_platform.h"
  14. #if !SANITIZER_APPLE
  15. #error "This file should only be compiled on Darwin."
  16. #endif
  17. #include <AvailabilityMacros.h>
  18. #include <CoreFoundation/CFBase.h>
  19. #include <dlfcn.h>
  20. #include <malloc/malloc.h>
  21. #include <sys/mman.h>
  22. #include "interception/interception.h"
  23. #include "sanitizer_common/sanitizer_allocator_dlsym.h"
  24. #include "sanitizer_common/sanitizer_mac.h"
  25. // Similar code is used in Google Perftools,
  26. // https://github.com/gperftools/gperftools.
  27. namespace __sanitizer {
  28. extern malloc_zone_t sanitizer_zone;
  29. struct sanitizer_malloc_introspection_t : public malloc_introspection_t {
  30. // IMPORTANT: Do not change the order, alignment, or types of these fields to
  31. // maintain binary compatibility. You should only add fields to this struct.
  32. // Used to track changes to the allocator that will affect
  33. // zone enumeration.
  34. u64 allocator_enumeration_version;
  35. uptr allocator_ptr;
  36. uptr allocator_size;
  37. };
  38. u64 GetMallocZoneAllocatorEnumerationVersion() {
  39. // This represents the current allocator ABI version.
  40. // This field should be incremented every time the Allocator
  41. // ABI changes in a way that breaks allocator enumeration.
  42. return 0;
  43. }
  44. } // namespace __sanitizer
  45. INTERCEPTOR(malloc_zone_t *, malloc_create_zone,
  46. vm_size_t start_size, unsigned zone_flags) {
  47. COMMON_MALLOC_ENTER();
  48. uptr page_size = GetPageSizeCached();
  49. uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size);
  50. COMMON_MALLOC_MEMALIGN(page_size, allocated_size);
  51. malloc_zone_t *new_zone = (malloc_zone_t *)p;
  52. internal_memcpy(new_zone, &sanitizer_zone, sizeof(sanitizer_zone));
  53. new_zone->zone_name = NULL; // The name will be changed anyway.
  54. // Prevent the client app from overwriting the zone contents.
  55. // Library functions that need to modify the zone will set PROT_WRITE on it.
  56. // This matches the behavior of malloc_create_zone() on OSX 10.7 and higher.
  57. mprotect(new_zone, allocated_size, PROT_READ);
  58. // We're explicitly *NOT* registering the zone.
  59. return new_zone;
  60. }
  61. INTERCEPTOR(void, malloc_destroy_zone, malloc_zone_t *zone) {
  62. COMMON_MALLOC_ENTER();
  63. // We don't need to do anything here. We're not registering new zones, so we
  64. // don't to unregister. Just un-mprotect and free() the zone.
  65. uptr page_size = GetPageSizeCached();
  66. uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size);
  67. mprotect(zone, allocated_size, PROT_READ | PROT_WRITE);
  68. if (zone->zone_name) {
  69. COMMON_MALLOC_FREE((void *)zone->zone_name);
  70. }
  71. COMMON_MALLOC_FREE(zone);
  72. }
  73. INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) {
  74. COMMON_MALLOC_ENTER();
  75. return &sanitizer_zone;
  76. }
  77. INTERCEPTOR(malloc_zone_t *, malloc_zone_from_ptr, const void *ptr) {
  78. COMMON_MALLOC_ENTER();
  79. size_t size = sanitizer_zone.size(&sanitizer_zone, ptr);
  80. if (size) { // Claimed by sanitizer zone?
  81. return &sanitizer_zone;
  82. }
  83. return REAL(malloc_zone_from_ptr)(ptr);
  84. }
  85. INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) {
  86. // FIXME: ASan should support purgeable allocations.
  87. // https://github.com/google/sanitizers/issues/139
  88. COMMON_MALLOC_ENTER();
  89. return &sanitizer_zone;
  90. }
  91. INTERCEPTOR(void, malloc_make_purgeable, void *ptr) {
  92. // FIXME: ASan should support purgeable allocations. Ignoring them is fine
  93. // for now.
  94. COMMON_MALLOC_ENTER();
  95. }
  96. INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) {
  97. // FIXME: ASan should support purgeable allocations. Ignoring them is fine
  98. // for now.
  99. COMMON_MALLOC_ENTER();
  100. // Must return 0 if the contents were not purged since the last call to
  101. // malloc_make_purgeable().
  102. return 0;
  103. }
  104. INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) {
  105. COMMON_MALLOC_ENTER();
  106. InternalScopedString new_name;
  107. if (name && zone->introspect == sanitizer_zone.introspect) {
  108. new_name.append(COMMON_MALLOC_ZONE_NAME "-%s", name);
  109. name = new_name.data();
  110. }
  111. // Call the system malloc's implementation for both external and our zones,
  112. // since that appropriately changes VM region protections on the zone.
  113. REAL(malloc_set_zone_name)(zone, name);
  114. }
  115. INTERCEPTOR(void *, malloc, size_t size) {
  116. COMMON_MALLOC_ENTER();
  117. COMMON_MALLOC_MALLOC(size);
  118. return p;
  119. }
  120. INTERCEPTOR(void, free, void *ptr) {
  121. COMMON_MALLOC_ENTER();
  122. if (!ptr) return;
  123. COMMON_MALLOC_FREE(ptr);
  124. }
  125. INTERCEPTOR(void *, realloc, void *ptr, size_t size) {
  126. COMMON_MALLOC_ENTER();
  127. COMMON_MALLOC_REALLOC(ptr, size);
  128. return p;
  129. }
  130. INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) {
  131. COMMON_MALLOC_ENTER();
  132. COMMON_MALLOC_CALLOC(nmemb, size);
  133. return p;
  134. }
  135. INTERCEPTOR(void *, valloc, size_t size) {
  136. COMMON_MALLOC_ENTER();
  137. COMMON_MALLOC_VALLOC(size);
  138. return p;
  139. }
  140. INTERCEPTOR(size_t, malloc_good_size, size_t size) {
  141. COMMON_MALLOC_ENTER();
  142. return sanitizer_zone.introspect->good_size(&sanitizer_zone, size);
  143. }
  144. INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) {
  145. COMMON_MALLOC_ENTER();
  146. CHECK(memptr);
  147. COMMON_MALLOC_POSIX_MEMALIGN(memptr, alignment, size);
  148. return res;
  149. }
  150. namespace {
  151. // TODO(glider): the __sanitizer_mz_* functions should be united with the Linux
  152. // wrappers, as they are basically copied from there.
  153. extern "C"
  154. SANITIZER_INTERFACE_ATTRIBUTE
  155. size_t __sanitizer_mz_size(malloc_zone_t* zone, const void* ptr) {
  156. COMMON_MALLOC_SIZE(ptr);
  157. return size;
  158. }
  159. extern "C"
  160. SANITIZER_INTERFACE_ATTRIBUTE
  161. void *__sanitizer_mz_malloc(malloc_zone_t *zone, uptr size) {
  162. COMMON_MALLOC_ENTER();
  163. COMMON_MALLOC_MALLOC(size);
  164. return p;
  165. }
  166. struct DlsymAlloc : public DlSymAllocator<DlsymAlloc> {
  167. static bool UseImpl() { return !COMMON_MALLOC_SANITIZER_INITIALIZED; }
  168. };
  169. extern "C"
  170. SANITIZER_INTERFACE_ATTRIBUTE
  171. void *__sanitizer_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
  172. if (DlsymAlloc::Use())
  173. return DlsymAlloc::Callocate(nmemb, size);
  174. COMMON_MALLOC_CALLOC(nmemb, size);
  175. return p;
  176. }
  177. extern "C"
  178. SANITIZER_INTERFACE_ATTRIBUTE
  179. void *__sanitizer_mz_valloc(malloc_zone_t *zone, size_t size) {
  180. COMMON_MALLOC_ENTER();
  181. COMMON_MALLOC_VALLOC(size);
  182. return p;
  183. }
  184. // TODO(glider): the allocation callbacks need to be refactored.
  185. extern "C"
  186. SANITIZER_INTERFACE_ATTRIBUTE
  187. void __sanitizer_mz_free(malloc_zone_t *zone, void *ptr) {
  188. if (!ptr) return;
  189. if (DlsymAlloc::PointerIsMine(ptr))
  190. return DlsymAlloc::Free(ptr);
  191. COMMON_MALLOC_FREE(ptr);
  192. }
  193. #define GET_ZONE_FOR_PTR(ptr) \
  194. malloc_zone_t *zone_ptr = WRAP(malloc_zone_from_ptr)(ptr); \
  195. const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name
  196. extern "C"
  197. SANITIZER_INTERFACE_ATTRIBUTE
  198. void *__sanitizer_mz_realloc(malloc_zone_t *zone, void *ptr, size_t new_size) {
  199. if (!ptr) {
  200. COMMON_MALLOC_MALLOC(new_size);
  201. return p;
  202. } else {
  203. COMMON_MALLOC_SIZE(ptr);
  204. if (size) {
  205. COMMON_MALLOC_REALLOC(ptr, new_size);
  206. return p;
  207. } else {
  208. // We can't recover from reallocating an unknown address, because
  209. // this would require reading at most |new_size| bytes from
  210. // potentially unaccessible memory.
  211. GET_ZONE_FOR_PTR(ptr);
  212. COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name);
  213. return nullptr;
  214. }
  215. }
  216. }
  217. extern "C"
  218. SANITIZER_INTERFACE_ATTRIBUTE
  219. void __sanitizer_mz_destroy(malloc_zone_t* zone) {
  220. // A no-op -- we will not be destroyed!
  221. Report("__sanitizer_mz_destroy() called -- ignoring\n");
  222. }
  223. extern "C"
  224. SANITIZER_INTERFACE_ATTRIBUTE
  225. void *__sanitizer_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
  226. COMMON_MALLOC_ENTER();
  227. COMMON_MALLOC_MEMALIGN(align, size);
  228. return p;
  229. }
  230. // This public API exists purely for testing purposes.
  231. extern "C"
  232. SANITIZER_INTERFACE_ATTRIBUTE
  233. malloc_zone_t* __sanitizer_mz_default_zone() {
  234. return &sanitizer_zone;
  235. }
  236. // This function is currently unused, and we build with -Werror.
  237. #if 0
  238. void __sanitizer_mz_free_definite_size(
  239. malloc_zone_t* zone, void *ptr, size_t size) {
  240. // TODO(glider): check that |size| is valid.
  241. UNIMPLEMENTED();
  242. }
  243. #endif
  244. #ifndef COMMON_MALLOC_HAS_ZONE_ENUMERATOR
  245. #error "COMMON_MALLOC_HAS_ZONE_ENUMERATOR must be defined"
  246. #endif
  247. static_assert((COMMON_MALLOC_HAS_ZONE_ENUMERATOR) == 0 ||
  248. (COMMON_MALLOC_HAS_ZONE_ENUMERATOR) == 1,
  249. "COMMON_MALLOC_HAS_ZONE_ENUMERATOR must be 0 or 1");
  250. #if COMMON_MALLOC_HAS_ZONE_ENUMERATOR
  251. // Forward declare and expect the implementation to provided by
  252. // includer.
  253. kern_return_t mi_enumerator(task_t task, void *, unsigned type_mask,
  254. vm_address_t zone_address, memory_reader_t reader,
  255. vm_range_recorder_t recorder);
  256. #else
  257. // Provide stub implementation that fails.
  258. kern_return_t mi_enumerator(task_t task, void *, unsigned type_mask,
  259. vm_address_t zone_address, memory_reader_t reader,
  260. vm_range_recorder_t recorder) {
  261. // Not supported.
  262. return KERN_FAILURE;
  263. }
  264. #endif
  265. #ifndef COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT
  266. #error "COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT must be defined"
  267. #endif
  268. static_assert((COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT) == 0 ||
  269. (COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT) == 1,
  270. "COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT must be 0 or 1");
  271. #if COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT
  272. // Forward declare and expect the implementation to provided by
  273. // includer.
  274. void mi_extra_init(
  275. sanitizer_malloc_introspection_t *mi);
  276. #else
  277. void mi_extra_init(
  278. sanitizer_malloc_introspection_t *mi) {
  279. // Just zero initialize the fields.
  280. mi->allocator_ptr = 0;
  281. mi->allocator_size = 0;
  282. }
  283. #endif
  284. size_t mi_good_size(malloc_zone_t *zone, size_t size) {
  285. // I think it's always safe to return size, but we maybe could do better.
  286. return size;
  287. }
  288. boolean_t mi_check(malloc_zone_t *zone) {
  289. UNIMPLEMENTED();
  290. }
  291. void mi_print(malloc_zone_t *zone, boolean_t verbose) {
  292. UNIMPLEMENTED();
  293. }
  294. void mi_log(malloc_zone_t *zone, void *address) {
  295. // I don't think we support anything like this
  296. }
  297. void mi_force_lock(malloc_zone_t *zone) {
  298. COMMON_MALLOC_FORCE_LOCK();
  299. }
  300. void mi_force_unlock(malloc_zone_t *zone) {
  301. COMMON_MALLOC_FORCE_UNLOCK();
  302. }
  303. void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
  304. COMMON_MALLOC_FILL_STATS(zone, stats);
  305. }
  306. boolean_t mi_zone_locked(malloc_zone_t *zone) {
  307. // UNIMPLEMENTED();
  308. return false;
  309. }
  310. } // unnamed namespace
  311. namespace COMMON_MALLOC_NAMESPACE {
  312. void InitMallocZoneFields() {
  313. static sanitizer_malloc_introspection_t sanitizer_zone_introspection;
  314. // Ok to use internal_memset, these places are not performance-critical.
  315. internal_memset(&sanitizer_zone_introspection, 0,
  316. sizeof(sanitizer_zone_introspection));
  317. sanitizer_zone_introspection.enumerator = &mi_enumerator;
  318. sanitizer_zone_introspection.good_size = &mi_good_size;
  319. sanitizer_zone_introspection.check = &mi_check;
  320. sanitizer_zone_introspection.print = &mi_print;
  321. sanitizer_zone_introspection.log = &mi_log;
  322. sanitizer_zone_introspection.force_lock = &mi_force_lock;
  323. sanitizer_zone_introspection.force_unlock = &mi_force_unlock;
  324. sanitizer_zone_introspection.statistics = &mi_statistics;
  325. sanitizer_zone_introspection.zone_locked = &mi_zone_locked;
  326. // Set current allocator enumeration version.
  327. sanitizer_zone_introspection.allocator_enumeration_version =
  328. GetMallocZoneAllocatorEnumerationVersion();
  329. // Perform any sanitizer specific initialization.
  330. mi_extra_init(&sanitizer_zone_introspection);
  331. internal_memset(&sanitizer_zone, 0, sizeof(malloc_zone_t));
  332. // Use version 6 for OSX >= 10.6.
  333. sanitizer_zone.version = 6;
  334. sanitizer_zone.zone_name = COMMON_MALLOC_ZONE_NAME;
  335. sanitizer_zone.size = &__sanitizer_mz_size;
  336. sanitizer_zone.malloc = &__sanitizer_mz_malloc;
  337. sanitizer_zone.calloc = &__sanitizer_mz_calloc;
  338. sanitizer_zone.valloc = &__sanitizer_mz_valloc;
  339. sanitizer_zone.free = &__sanitizer_mz_free;
  340. sanitizer_zone.realloc = &__sanitizer_mz_realloc;
  341. sanitizer_zone.destroy = &__sanitizer_mz_destroy;
  342. sanitizer_zone.batch_malloc = 0;
  343. sanitizer_zone.batch_free = 0;
  344. sanitizer_zone.free_definite_size = 0;
  345. sanitizer_zone.memalign = &__sanitizer_mz_memalign;
  346. sanitizer_zone.introspect = &sanitizer_zone_introspection;
  347. }
  348. void ReplaceSystemMalloc() {
  349. InitMallocZoneFields();
  350. // Register the zone.
  351. malloc_zone_register(&sanitizer_zone);
  352. }
  353. } // namespace COMMON_MALLOC_NAMESPACE