tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Zone.c (13821B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozmemory_wrap.h"
      8 
      9 #include <stdlib.h>
     10 #include <mach/mach_types.h>
     11 #include "mozilla/Assertions.h"
     12 
     13 // Malloc implementation functions are MOZ_MEMORY_API, and jemalloc
     14 // specific functions MOZ_JEMALLOC_API; see mozmemory_wrap.h
     15 
     16 #define MALLOC_DECL(name, return_type, ...) \
     17  MOZ_MEMORY_API return_type name##_impl(__VA_ARGS__);
     18 #define MALLOC_FUNCS MALLOC_FUNCS_MALLOC
     19 #include "malloc_decls.h"
     20 
     21 #define MALLOC_DECL(name, return_type, ...) \
     22  MOZ_JEMALLOC_API return_type name##_impl(__VA_ARGS__);
     23 #define MALLOC_FUNCS MALLOC_FUNCS_JEMALLOC
     24 #include "malloc_decls.h"
     25 
     26 // Definitions of the following structs in malloc/malloc.h might be too old
     27 // for the built binary to run on newer versions of OSX. So use the newest
     28 // possible version of those structs.
     29 
     30 typedef struct _malloc_zone_t {
     31  void* reserved1;
     32  void* reserved2;
     33  size_t (*size)(struct _malloc_zone_t*, const void*);
     34  void* (*malloc)(struct _malloc_zone_t*, size_t);
     35  void* (*calloc)(struct _malloc_zone_t*, size_t, size_t);
     36  void* (*valloc)(struct _malloc_zone_t*, size_t);
     37  void (*free)(struct _malloc_zone_t*, void*);
     38  void* (*realloc)(struct _malloc_zone_t*, void*, size_t);
     39  void (*destroy)(struct _malloc_zone_t*);
     40  const char* zone_name;
     41  unsigned (*batch_malloc)(struct _malloc_zone_t*, size_t, void**, unsigned);
     42  void (*batch_free)(struct _malloc_zone_t*, void**, unsigned);
     43  struct malloc_introspection_t* introspect;
     44  unsigned version;
     45  void* (*memalign)(struct _malloc_zone_t*, size_t, size_t);
     46  void (*free_definite_size)(struct _malloc_zone_t*, void*, size_t);
     47  size_t (*pressure_relief)(struct _malloc_zone_t*, size_t);
     48 } malloc_zone_t;
     49 
     50 typedef struct {
     51  vm_address_t address;
     52  vm_size_t size;
     53 } vm_range_t;
     54 
     55 typedef struct malloc_statistics_t {
     56  unsigned blocks_in_use;
     57  size_t size_in_use;
     58  size_t max_size_in_use;
     59  size_t size_allocated;
     60 } malloc_statistics_t;
     61 
     62 typedef kern_return_t memory_reader_t(task_t, vm_address_t, vm_size_t, void**);
     63 
     64 typedef void vm_range_recorder_t(task_t, void*, unsigned type, vm_range_t*,
     65                                 unsigned);
     66 
     67 typedef struct malloc_introspection_t {
     68  kern_return_t (*enumerator)(task_t, void*, unsigned, vm_address_t,
     69                              memory_reader_t, vm_range_recorder_t);
     70  size_t (*good_size)(malloc_zone_t*, size_t);
     71  boolean_t (*check)(malloc_zone_t*);
     72  void (*print)(malloc_zone_t*, boolean_t);
     73  void (*log)(malloc_zone_t*, void*);
     74  void (*force_lock)(malloc_zone_t*);
     75  void (*force_unlock)(malloc_zone_t*);
     76  void (*statistics)(malloc_zone_t*, malloc_statistics_t*);
     77  boolean_t (*zone_locked)(malloc_zone_t*);
     78  boolean_t (*enable_discharge_checking)(malloc_zone_t*);
     79  boolean_t (*disable_discharge_checking)(malloc_zone_t*);
     80  void (*discharge)(malloc_zone_t*, void*);
     81 #ifdef __BLOCKS__
     82  void (*enumerate_discharged_pointers)(malloc_zone_t*, void (^)(void*, void*));
     83 #else
     84  void* enumerate_unavailable_without_blocks;
     85 #endif
     86  void (*reinit_lock)(malloc_zone_t*);
     87 } malloc_introspection_t;
     88 
     89 extern kern_return_t malloc_get_all_zones(task_t, memory_reader_t,
     90                                          vm_address_t**, unsigned*);
     91 
     92 extern malloc_zone_t* malloc_default_zone(void);
     93 
     94 extern void malloc_zone_register(malloc_zone_t* zone);
     95 
     96 extern void malloc_zone_unregister(malloc_zone_t* zone);
     97 
     98 extern malloc_zone_t* malloc_default_purgeable_zone(void);
     99 
    100 extern malloc_zone_t* malloc_zone_from_ptr(const void* ptr);
    101 
    102 extern void malloc_zone_free(malloc_zone_t* zone, void* ptr);
    103 
    104 extern void* malloc_zone_realloc(malloc_zone_t* zone, void* ptr, size_t size);
    105 
    106 // The following is a OSX zone allocator implementation.
    107 // /!\ WARNING. It assumes the underlying malloc implementation's
    108 // malloc_usable_size returns 0 when the given pointer is not owned by
    109 // the allocator. Sadly, OSX does call zone_size with pointers not
    110 // owned by the allocator.
    111 
    112 static size_t zone_size(malloc_zone_t* zone, const void* ptr) {
    113  return malloc_usable_size_impl(ptr);
    114 }
    115 
    116 static void* zone_malloc(malloc_zone_t* zone, size_t size) {
    117  return malloc_impl(size);
    118 }
    119 
    120 static void* zone_calloc(malloc_zone_t* zone, size_t num, size_t size) {
    121  return calloc_impl(num, size);
    122 }
    123 
    124 static void* zone_realloc(malloc_zone_t* zone, void* ptr, size_t size) {
    125  if (malloc_usable_size_impl(ptr)) return realloc_impl(ptr, size);
    126 
    127  // Sometimes, system libraries call malloc_zone_* functions with the wrong
    128  // zone (e.g. CoreFoundation does). In that case, we need to find the real
    129  // one. We can't call libSystem's realloc directly because we're exporting
    130  // realloc from libmozglue and we'd pick that one, so we manually find the
    131  // right zone and realloc with it.
    132  malloc_zone_t* real_zone = malloc_zone_from_ptr(ptr);
    133  // The system allocator crashes voluntarily by default when a pointer can't
    134  // be traced back to a zone. Do the same.
    135  MOZ_RELEASE_ASSERT(real_zone);
    136  MOZ_RELEASE_ASSERT(real_zone != zone);
    137  return malloc_zone_realloc(real_zone, ptr, size);
    138 }
    139 
    140 static void other_zone_free(malloc_zone_t* original_zone, void* ptr) {
    141  // Sometimes, system libraries call malloc_zone_* functions with the wrong
    142  // zone (e.g. CoreFoundation does). In that case, we need to find the real
    143  // one. We can't call libSystem's free directly because we're exporting
    144  // free from libmozglue and we'd pick that one, so we manually find the
    145  // right zone and free with it.
    146  if (!ptr) {
    147    return;
    148  }
    149  malloc_zone_t* zone = malloc_zone_from_ptr(ptr);
    150  // The system allocator crashes voluntarily by default when a pointer can't
    151  // be traced back to a zone. Do the same.
    152  MOZ_RELEASE_ASSERT(zone);
    153  MOZ_RELEASE_ASSERT(zone != original_zone);
    154  return malloc_zone_free(zone, ptr);
    155 }
    156 
    157 static void zone_free(malloc_zone_t* zone, void* ptr) {
    158  if (malloc_usable_size_impl(ptr)) {
    159    free_impl(ptr);
    160    return;
    161  }
    162  other_zone_free(zone, ptr);
    163 }
    164 
    165 static void zone_free_definite_size(malloc_zone_t* zone, void* ptr,
    166                                    size_t size) {
    167  size_t current_size = malloc_usable_size_impl(ptr);
    168  if (current_size) {
    169    MOZ_ASSERT(current_size == size);
    170    free_impl(ptr);
    171    return;
    172  }
    173  other_zone_free(zone, ptr);
    174 }
    175 
    176 static void* zone_memalign(malloc_zone_t* zone, size_t alignment, size_t size) {
    177  void* ptr;
    178  if (posix_memalign_impl(&ptr, alignment, size) == 0) return ptr;
    179  return NULL;
    180 }
    181 
    182 static void* zone_valloc(malloc_zone_t* zone, size_t size) {
    183  return valloc_impl(size);
    184 }
    185 
    186 static void zone_destroy(malloc_zone_t* zone) {
    187  // This function should never be called.
    188  MOZ_CRASH();
    189 }
    190 
    191 static unsigned zone_batch_malloc(malloc_zone_t* zone, size_t size,
    192                                  void** results, unsigned num_requested) {
    193  unsigned i;
    194 
    195  for (i = 0; i < num_requested; i++) {
    196    results[i] = malloc_impl(size);
    197    if (!results[i]) break;
    198  }
    199 
    200  return i;
    201 }
    202 
    203 static void zone_batch_free(malloc_zone_t* zone, void** to_be_freed,
    204                            unsigned num_to_be_freed) {
    205  unsigned i;
    206 
    207  for (i = 0; i < num_to_be_freed; i++) {
    208    zone_free(zone, to_be_freed[i]);
    209    to_be_freed[i] = NULL;
    210  }
    211 }
    212 
    213 static size_t zone_pressure_relief(malloc_zone_t* zone, size_t goal) {
    214  return 0;
    215 }
    216 
    217 static size_t zone_good_size(malloc_zone_t* zone, size_t size) {
    218  return malloc_good_size_impl(size);
    219 }
    220 
    221 static kern_return_t zone_enumerator(task_t task, void* data,
    222                                     unsigned type_mask,
    223                                     vm_address_t zone_address,
    224                                     memory_reader_t reader,
    225                                     vm_range_recorder_t recorder) {
    226  return KERN_SUCCESS;
    227 }
    228 
    229 static boolean_t zone_check(malloc_zone_t* zone) { return true; }
    230 
    231 static void zone_print(malloc_zone_t* zone, boolean_t verbose) {}
    232 
    233 static void zone_log(malloc_zone_t* zone, void* address) {}
    234 
    235 // On Darwin the postfork handler is called in both the parent and the child.
    236 extern void _malloc_prefork(void);
    237 extern void _malloc_postfork(void);
    238 
    239 static void zone_force_lock(malloc_zone_t* zone) {
    240  // /!\ This calls into mozjemalloc. It works because we're linked in the
    241  // same library.
    242  _malloc_prefork();
    243 }
    244 
    245 static void zone_force_unlock(malloc_zone_t* zone) {
    246  // /!\ This calls into mozjemalloc. It works because we're linked in the
    247  // same library.
    248  _malloc_postfork();
    249 }
    250 
    251 static void zone_statistics(malloc_zone_t* zone, malloc_statistics_t* stats) {
    252  // We make no effort to actually fill the values
    253  stats->blocks_in_use = 0;
    254  stats->size_in_use = 0;
    255  stats->max_size_in_use = 0;
    256  stats->size_allocated = 0;
    257 }
    258 
    259 static boolean_t zone_locked(malloc_zone_t* zone) {
    260  // Pretend no lock is being held
    261  return false;
    262 }
    263 
    264 static void zone_reinit_lock(malloc_zone_t* zone) {
    265  // As of OSX 10.12, this function is only used when force_unlock would
    266  // be used if the zone version were < 9. So just use force_unlock.
    267  zone_force_unlock(zone);
    268 }
    269 
    270 static malloc_zone_t zone;
    271 static struct malloc_introspection_t zone_introspect;
    272 
    273 static malloc_zone_t* get_default_zone() {
    274  malloc_zone_t** zones = NULL;
    275  unsigned int num_zones = 0;
    276 
    277  // On OSX 10.12, malloc_default_zone returns a special zone that is not
    278  // present in the list of registered zones. That zone uses a "lite zone"
    279  // if one is present (apparently enabled when malloc stack logging is
    280  // enabled), or the first registered zone otherwise. In practice this
    281  // means unless malloc stack logging is enabled, the first registered
    282  // zone is the default.
    283  // So get the list of zones to get the first one, instead of relying on
    284  // malloc_default_zone.
    285  if (KERN_SUCCESS !=
    286      malloc_get_all_zones(0, NULL, (vm_address_t**)&zones, &num_zones)) {
    287    // Reset the value in case the failure happened after it was set.
    288    num_zones = 0;
    289  }
    290  if (num_zones) {
    291    return zones[0];
    292  }
    293  return malloc_default_zone();
    294 }
    295 
    296 __attribute__((constructor)) static void register_zone(void) {
    297  malloc_zone_t* default_zone = get_default_zone();
    298 
    299  zone.size = zone_size;
    300  zone.malloc = zone_malloc;
    301  zone.calloc = zone_calloc;
    302  zone.valloc = zone_valloc;
    303  zone.free = zone_free;
    304  zone.realloc = zone_realloc;
    305  zone.destroy = zone_destroy;
    306 #ifdef MOZ_REPLACE_MALLOC
    307  zone.zone_name = "replace_malloc_zone";
    308 #else
    309  zone.zone_name = "jemalloc_zone";
    310 #endif
    311  zone.batch_malloc = zone_batch_malloc;
    312  zone.batch_free = zone_batch_free;
    313  zone.introspect = &zone_introspect;
    314  zone.version = 9;
    315  zone.memalign = zone_memalign;
    316  zone.free_definite_size = zone_free_definite_size;
    317  zone.pressure_relief = zone_pressure_relief;
    318  zone_introspect.enumerator = zone_enumerator;
    319  zone_introspect.good_size = zone_good_size;
    320  zone_introspect.check = zone_check;
    321  zone_introspect.print = zone_print;
    322  zone_introspect.log = zone_log;
    323  zone_introspect.force_lock = zone_force_lock;
    324  zone_introspect.force_unlock = zone_force_unlock;
    325  zone_introspect.statistics = zone_statistics;
    326  zone_introspect.zone_locked = zone_locked;
    327  zone_introspect.enable_discharge_checking = NULL;
    328  zone_introspect.disable_discharge_checking = NULL;
    329  zone_introspect.discharge = NULL;
    330 #ifdef __BLOCKS__
    331  zone_introspect.enumerate_discharged_pointers = NULL;
    332 #else
    333  zone_introspect.enumerate_unavailable_without_blocks = NULL;
    334 #endif
    335  zone_introspect.reinit_lock = zone_reinit_lock;
    336 
    337  // The default purgeable zone is created lazily by OSX's libc.  It uses
    338  // the default zone when it is created for "small" allocations
    339  // (< 15 KiB), but assumes the default zone is a scalable_zone.  This
    340  // obviously fails when the default zone is the jemalloc zone, so
    341  // malloc_default_purgeable_zone is called beforehand so that the
    342  // default purgeable zone is created when the default zone is still
    343  // a scalable_zone.
    344  malloc_zone_t* purgeable_zone = malloc_default_purgeable_zone();
    345 
    346  // There is a problem related to the above with the system nano zone, which
    347  // is hard to work around from here, and that is instead worked around by
    348  // disabling the nano zone through an environment variable
    349  // (MallocNanoZone=0). In Firefox, we do that through
    350  // browser/app/macbuild/Contents/Info.plist.in.
    351 
    352  // Register the custom zone.  At this point it won't be the default.
    353  malloc_zone_register(&zone);
    354 
    355  do {
    356    // Unregister and reregister the default zone.  On OSX >= 10.6,
    357    // unregistering takes the last registered zone and places it at the
    358    // location of the specified zone.  Unregistering the default zone thus
    359    // makes the last registered one the default.  On OSX < 10.6,
    360    // unregistering shifts all registered zones.  The first registered zone
    361    // then becomes the default.
    362    malloc_zone_unregister(default_zone);
    363    malloc_zone_register(default_zone);
    364 
    365    // On OSX 10.6, having the default purgeable zone appear before the default
    366    // zone makes some things crash because it thinks it owns the default
    367    // zone allocated pointers. We thus unregister/re-register it in order to
    368    // ensure it's always after the default zone. On OSX < 10.6, as
    369    // unregistering shifts registered zones, this simply removes the purgeable
    370    // zone from the list and adds it back at the end, after the default zone.
    371    // On OSX >= 10.6, unregistering replaces the purgeable zone with the last
    372    // registered zone above, i.e the default zone. Registering it again then
    373    // puts it at the end, obviously after the default zone.
    374    malloc_zone_unregister(purgeable_zone);
    375    malloc_zone_register(purgeable_zone);
    376    default_zone = get_default_zone();
    377  } while (default_zone != &zone);
    378 }