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)