tor-browser

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

pthread_atfork_interposer.cpp (6437B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include <atomic>
      6 #include <array>
      7 #include <errno.h>
      8 
      9 #include "mozilla/Assertions.h"
     10 #include "mozilla/DebugOnly.h"
     11 
     12 #include "InterposerHelper.h"
     13 
     14 using mozilla::DebugOnly;
     15 
     16 #if defined(MOZ_ENABLE_FORKSERVER) && !defined(MOZ_TSAN)
     17 #  include "mozilla/pthread_atfork.h"
     18 
     19 static constexpr const int maxHandlers = 32;
     20 static constexpr const int idxPreFork = 0;
     21 static constexpr const int idxPostForkParent = 1;
     22 static constexpr const int idxPostForkChild = 2;
     23 
     24 struct moz_pthread_atfork_handler {
     25  using fn_ptr = std::atomic<void (*)(void)>;
     26  using dso_handle = std::atomic<void*>;
     27  using pthread_handlers = std::array<fn_ptr, 3>;
     28 
     29  std::atomic<int> usedElems = 0;
     30  std::array<pthread_handlers, maxHandlers> handlers = {};
     31  std::array<dso_handle, maxHandlers> dsos = {};
     32 
     33  bool add(void (*aPrefork)(void), void (*aParent)(void), void (*aChild)(void),
     34           const void* const aHandle) {
     35    if (usedElems == maxHandlers) {
     36      return false;
     37    }
     38 
     39    int elem = 0;
     40    for (elem = 0; elem < maxHandlers; ++elem) {
     41      if (dsos[elem] == nullptr) {
     42        handlers[elem][idxPreFork] = aPrefork;
     43        handlers[elem][idxPostForkParent] = aParent;
     44        handlers[elem][idxPostForkChild] = aChild;
     45        dsos[elem] = (void*)(aHandle);
     46        ++usedElems;
     47        break;
     48      }
     49    }
     50 
     51    return true;
     52  }
     53 
     54  bool remove(void* aHandle) {
     55    int elem = 0;
     56    for (elem = 0; elem < maxHandlers; ++elem) {
     57      if (dsos[elem] == aHandle) {
     58        handlers[elem][idxPreFork] = nullptr;
     59        handlers[elem][idxPostForkParent] = nullptr;
     60        handlers[elem][idxPostForkChild] = nullptr;
     61        dsos[elem] = nullptr;
     62        --usedElems;
     63      }
     64    }
     65 
     66    return true;
     67  }
     68 };
     69 
     70 struct moz_pthread_atfork_handler mozPthreadHandlers;
     71 
     72 #  if defined(LIBC_GLIBC)
     73 // On glibc the pthread_atfork may be available only from libc_nonshared.a
     74 // so prefer interposing the linker-resolved __register_atfork()
     75 
     76 extern const void* const __dso_handle;
     77 using register_atfork_t = int (*)(void (*)(), void (*)(), void (*)(),
     78                                  const void* const);
     79 static register_atfork_t real_register_atfork = nullptr;
     80 #  else
     81 using pthread_atfork_t = int (*)(void (*)(), void (*)(), void (*)());
     82 static pthread_atfork_t real_pthread_atfork = nullptr;
     83 #  endif
     84 
     85 static int notReadyCount = 0;
     86 
     87 extern "C" {
     88 
     89 #  if defined(LIBC_GLIBC)
     90 MFBT_API int __register_atfork(void (*aPrefork)(void),
     91                               void (*aPostForkParent)(void),
     92                               void (*aPostForkChild)(void),
     93                               const void* const dso_handle)
     94 #  else
     95 MFBT_API int pthread_atfork(void (*aPrefork)(void),
     96                            void (*aPostForkParent)(void),
     97                            void (*aPostForkChild)(void))
     98 #  endif
     99 {
    100 #  if defined(LIBC_GLIBC)
    101  MOZ_ASSERT(real_register_atfork != __register_atfork,
    102             "Found __register_atfork from libc");
    103 #  else
    104  MOZ_ASSERT(real_pthread_atfork != pthread_atfork,
    105             "Found pthread_atfork from libc");
    106 #  endif
    107 
    108  int rv = 0;
    109 #  if defined(LIBC_GLIBC)
    110  if (real_register_atfork) {
    111    real_register_atfork(aPrefork, aPostForkParent, aPostForkChild, dso_handle);
    112 #  else
    113  if (real_pthread_atfork) {
    114    real_pthread_atfork(aPrefork, aPostForkParent, aPostForkChild);
    115 #  endif
    116    MOZ_ASSERT(rv == 0, "call to real_register_atfork() failed");
    117    if (rv != 0) {
    118      return rv;
    119    }
    120  } else {
    121    ++notReadyCount;
    122  }
    123 
    124  rv = mozPthreadHandlers.add(aPrefork, aPostForkParent, aPostForkChild
    125 #  if defined(LIBC_GLIBC)
    126                              ,
    127                              dso_handle
    128 #  else
    129                              ,
    130                              (void*)(1)
    131 #  endif
    132                              )
    133           ? 0
    134           : 1;
    135  MOZ_ASSERT(rv == 0,
    136 #  if defined(LIBC_GLIBC)
    137             "Should have been able to add to __register_atfork() handlers"
    138 #  else
    139             "Should have been able to add to pthread_atfork() handlers"
    140 #  endif
    141  );
    142 
    143  if (rv > 0) {
    144    rv = ENOMEM;
    145  }
    146 
    147  return rv;
    148 }
    149 
    150 #  if defined(LIBC_GLIBC)
    151 MFBT_API void __cxa_finalize(void* handle) {
    152  static const auto real_cxa_finalize = GET_REAL_SYMBOL(__cxa_finalize);
    153  real_cxa_finalize(handle);
    154  mozPthreadHandlers.remove(handle);
    155 }
    156 #  endif
    157 }
    158 
    159 #  if defined(LIBC_GLIBC)
    160 __attribute__((used)) __attribute__((constructor)) void register_atfork_setup(
    161    void) {
    162  real_register_atfork = GET_REAL_SYMBOL(__register_atfork);
    163 
    164  if (notReadyCount > 0) {
    165    for (int i = 0; i < notReadyCount; ++i) {
    166      real_register_atfork(mozPthreadHandlers.handlers[i][idxPreFork],
    167                           mozPthreadHandlers.handlers[i][idxPostForkParent],
    168                           mozPthreadHandlers.handlers[i][idxPostForkChild],
    169                           __dso_handle);
    170    }
    171  }
    172 }
    173 #  else
    174 __attribute__((used)) __attribute__((constructor)) void pthread_atfork_setup(
    175    void) {
    176  real_pthread_atfork = GET_REAL_SYMBOL(pthread_atfork);
    177 
    178  if (notReadyCount > 0) {
    179    for (int i = 0; i < notReadyCount; ++i) {
    180      real_pthread_atfork(mozPthreadHandlers.handlers[i][idxPreFork],
    181                          mozPthreadHandlers.handlers[i][idxPostForkParent],
    182                          mozPthreadHandlers.handlers[i][idxPostForkChild]);
    183    }
    184  }
    185 }
    186 #  endif
    187 
    188 void run_moz_pthread_atfork_handlers(struct moz_pthread_atfork_handler* list,
    189                                     int handlerIdx, bool reverse) {
    190  MOZ_ASSERT(list, "moz_pthread_atfork_handler should not be nullptr");
    191  for (int i = (reverse ? maxHandlers - 1 : 0);
    192       (reverse ? (i >= 0) : (i < maxHandlers)); (reverse ? --i : ++i)) {
    193    if (list->dsos[i]) {
    194      if (list->handlers[i][handlerIdx]) {
    195        (*list->handlers[i][handlerIdx])();
    196      }
    197    }
    198  }
    199 }
    200 
    201 void run_moz_pthread_atfork_handlers_prefork() {
    202  run_moz_pthread_atfork_handlers(&mozPthreadHandlers, idxPreFork, true);
    203 }
    204 
    205 void run_moz_pthread_atfork_handlers_postfork_parent() {
    206  run_moz_pthread_atfork_handlers(&mozPthreadHandlers, idxPostForkParent,
    207                                  false);
    208 }
    209 
    210 void run_moz_pthread_atfork_handlers_postfork_child() {
    211  run_moz_pthread_atfork_handlers(&mozPthreadHandlers, idxPostForkChild, false);
    212 }
    213 #endif  // defined(MOZ_ENABLE_FORKSERVER)