mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-17 06:09:19 +00:00
Bug 1917575 - Interpose __register_atfork()/pthread_atfork() and run it after clone() r=jld
Differential Revision: https://phabricator.services.mozilla.com/D221387
This commit is contained in:
parent
b13b61ca3a
commit
9a1103df59
@ -17,6 +17,15 @@ if CONFIG["MOZ_CRASHREPORTER"]:
|
||||
"pthread_create_interposer.cpp",
|
||||
]
|
||||
|
||||
if CONFIG["MOZ_SANDBOX"]:
|
||||
EXPORTS.mozilla += [
|
||||
"pthread_atfork.h",
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
"pthread_atfork_interposer.cpp",
|
||||
]
|
||||
|
||||
if CONFIG["MOZ_LINKER"]:
|
||||
LOCAL_INCLUDES += [
|
||||
"/mozglue/linker",
|
||||
|
21
mozglue/interposers/pthread_atfork.h
Normal file
21
mozglue/interposers/pthread_atfork.h
Normal file
@ -0,0 +1,21 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef __mozilla_pthread_atfork_h_
|
||||
#define __mozilla_pthread_atfork_h_
|
||||
|
||||
#if defined(MOZ_ENABLE_FORKSERVER)
|
||||
|
||||
# if defined(__GLIBC__) && !defined(__UCLIBC__)
|
||||
// We really are using glibc, not uClibc pretending to be glibc.
|
||||
# define LIBC_GLIBC 1
|
||||
# endif
|
||||
|
||||
extern MFBT_API void run_moz_pthread_atfork_handlers_prefork();
|
||||
extern MFBT_API void run_moz_pthread_atfork_handlers_postfork_parent();
|
||||
extern MFBT_API void run_moz_pthread_atfork_handlers_postfork_child();
|
||||
|
||||
#endif // defined(MOZ_ENABLE_FORKSERVER)
|
||||
#endif // __mozilla_pthread_atfork_h_
|
254
mozglue/interposers/pthread_atfork_interposer.cpp
Normal file
254
mozglue/interposers/pthread_atfork_interposer.cpp
Normal file
@ -0,0 +1,254 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <atomic>
|
||||
#include <array>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
|
||||
#include "InterposerHelper.h"
|
||||
|
||||
using mozilla::DebugOnly;
|
||||
|
||||
#if defined(MOZ_ENABLE_FORKSERVER) && !defined(MOZ_TSAN)
|
||||
# include "mozilla/pthread_atfork.h"
|
||||
|
||||
static constexpr const int maxHandlers = 16;
|
||||
static constexpr const int idxPreFork = 0;
|
||||
static constexpr const int idxPostForkParent = 1;
|
||||
static constexpr const int idxPostForkChild = 2;
|
||||
|
||||
struct moz_pthread_atfork_handler {
|
||||
using fn_ptr = std::atomic<void (*)(void)>;
|
||||
using dso_handle = std::atomic<void*>;
|
||||
using pthread_handlers = std::array<fn_ptr, 3>;
|
||||
|
||||
std::atomic<int> usedElems = 0;
|
||||
std::array<pthread_handlers, maxHandlers> handlers = {};
|
||||
std::array<dso_handle, maxHandlers> dsos = {};
|
||||
|
||||
bool add(void (*aPrefork)(void), void (*aParent)(void), void (*aChild)(void),
|
||||
const void* const aHandle) {
|
||||
if (usedElems == maxHandlers) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int elem = 0;
|
||||
for (elem = 0; elem < maxHandlers; ++elem) {
|
||||
if (dsos[elem] == nullptr) {
|
||||
handlers[elem][idxPreFork] = aPrefork;
|
||||
handlers[elem][idxPostForkParent] = aParent;
|
||||
handlers[elem][idxPostForkChild] = aChild;
|
||||
dsos[elem] = (void*)(aHandle);
|
||||
++usedElems;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool remove(void* aHandle) {
|
||||
int elem = 0;
|
||||
for (elem = 0; elem < maxHandlers; ++elem) {
|
||||
if (dsos[elem] == aHandle) {
|
||||
handlers[elem][idxPreFork] = nullptr;
|
||||
handlers[elem][idxPostForkParent] = nullptr;
|
||||
handlers[elem][idxPostForkChild] = nullptr;
|
||||
dsos[elem] = nullptr;
|
||||
--usedElems;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct moz_pthread_atfork_handler mozPthreadHandlers;
|
||||
|
||||
# if defined(LIBC_GLIBC)
|
||||
// On glibc the pthread_atfork may be available only from libc_nonshared.a
|
||||
// so prefer interposing the linker-resolved __register_atfork()
|
||||
|
||||
extern const void* const __dso_handle;
|
||||
using register_atfork_t = int (*)(void (*)(), void (*)(), void (*)(),
|
||||
const void* const) noexcept;
|
||||
static register_atfork_t real_register_atfork = nullptr;
|
||||
|
||||
using cxa_finalize_t = void (*)(void*) noexcept;
|
||||
static cxa_finalize_t real_cxa_finalize = nullptr;
|
||||
# else
|
||||
using pthread_atfork_t = int (*)(void (*)(), void (*)(), void (*)()) noexcept;
|
||||
static pthread_atfork_t real_pthread_atfork = nullptr;
|
||||
# endif
|
||||
|
||||
static int notReadyCount = 0;
|
||||
|
||||
extern "C" {
|
||||
|
||||
# if defined(LIBC_GLIBC)
|
||||
MFBT_API int __register_atfork(void (*aPrefork)(void),
|
||||
void (*aPostForkParent)(void),
|
||||
void (*aPostForkChild)(void),
|
||||
const void* const dso_handle)
|
||||
# else
|
||||
MFBT_API int pthread_atfork(void (*aPrefork)(void),
|
||||
void (*aPostForkParent)(void),
|
||||
void (*aPostForkChild)(void))
|
||||
# endif
|
||||
{
|
||||
# if defined(LIBC_GLIBC)
|
||||
MOZ_ASSERT(real_register_atfork != __register_atfork,
|
||||
"Found __register_atfork from libc");
|
||||
# else
|
||||
MOZ_ASSERT(real_pthread_atfork != pthread_atfork,
|
||||
"Found pthread_atfork from libc");
|
||||
# endif
|
||||
|
||||
int rv = 0;
|
||||
# if defined(LIBC_GLIBC)
|
||||
if (real_register_atfork) {
|
||||
real_register_atfork(aPrefork, aPostForkParent, aPostForkChild, dso_handle);
|
||||
# else
|
||||
if (real_pthread_atfork) {
|
||||
real_pthread_atfork(aPrefork, aPostForkParent, aPostForkChild);
|
||||
# endif
|
||||
MOZ_ASSERT(rv == 0, "call to real_register_atfork() failed");
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
++notReadyCount;
|
||||
}
|
||||
|
||||
rv = mozPthreadHandlers.add(aPrefork, aPostForkParent, aPostForkChild
|
||||
# if defined(LIBC_GLIBC)
|
||||
,
|
||||
dso_handle
|
||||
# endif
|
||||
)
|
||||
? 0
|
||||
: 1;
|
||||
MOZ_ASSERT(rv == 0,
|
||||
# if defined(LIBC_GLIBC)
|
||||
"Should have been able to add to __register_atfork() handlers"
|
||||
# else
|
||||
"Should have been able to add to pthread_atfork() handlers"
|
||||
# endif
|
||||
);
|
||||
|
||||
if (rv > 0) {
|
||||
rv = ENOMEM;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
# if defined(LIBC_GLIBC)
|
||||
MFBT_API void __cxa_finalize(void* handle) {
|
||||
real_cxa_finalize(handle);
|
||||
mozPthreadHandlers.remove(handle);
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
# if defined(LIBC_GLIBC)
|
||||
__attribute__((used)) __attribute__((constructor)) void register_atfork_setup(
|
||||
void) {
|
||||
const char* glibc_version_register =
|
||||
# if defined(__x86_64__) || defined(__i386__)
|
||||
"GLIBC_2.3.2"
|
||||
# elif defined(__aarch64__)
|
||||
"GLIBC_2.17"
|
||||
# else
|
||||
# error \
|
||||
"Missing GLIBC version for __register_atfork(). Please objdump -tTC libc.so.6 and add"
|
||||
# endif
|
||||
;
|
||||
|
||||
// Use dlvsym() otherwise symbol resolution may find the interposed version
|
||||
real_register_atfork = (register_atfork_t)dlvsym(nullptr, "__register_atfork",
|
||||
glibc_version_register);
|
||||
|
||||
MOZ_ASSERT(real_register_atfork != nullptr, "Found real_register_atfork");
|
||||
MOZ_ASSERT(real_register_atfork != __register_atfork,
|
||||
"Found register_atfork from libc");
|
||||
|
||||
const char* glibc_version_finalize =
|
||||
# if defined(__i386__)
|
||||
"GLIBC_2.1.3"
|
||||
# elif defined(__x86_64__)
|
||||
"GLIBC_2.2.5"
|
||||
# elif defined(__aarch64__)
|
||||
"GLIBC_2.17"
|
||||
# else
|
||||
# error \
|
||||
"Missing GLIBC version for __cxa_finalize(). Please objdump -tTC libc.so.6 and add"
|
||||
# endif
|
||||
;
|
||||
// Use dlvsym() otherwise symbol resolution may find the interposed version
|
||||
real_cxa_finalize =
|
||||
(cxa_finalize_t)dlvsym(nullptr, "__cxa_finalize", glibc_version_finalize);
|
||||
|
||||
MOZ_ASSERT(real_cxa_finalize != nullptr, "Found real_cxa_finalize");
|
||||
MOZ_ASSERT(real_cxa_finalize != __cxa_finalize,
|
||||
"Found cxa_finalize from libc");
|
||||
|
||||
if (notReadyCount > 0) {
|
||||
for (int i = 0; i < notReadyCount; ++i) {
|
||||
real_register_atfork(mozPthreadHandlers.handlers[i][idxPreFork],
|
||||
mozPthreadHandlers.handlers[i][idxPostForkParent],
|
||||
mozPthreadHandlers.handlers[i][idxPostForkChild],
|
||||
__dso_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
# else
|
||||
__attribute__((used)) __attribute__((constructor)) void pthread_atfork_setup(
|
||||
void) {
|
||||
real_pthread_atfork = (pthread_atfork_t)dlsym(RTLD_NEXT, "pthread_atfork");
|
||||
|
||||
MOZ_ASSERT(real_pthread_atfork != nullptr, "Found real_pthread_atfork");
|
||||
MOZ_ASSERT(real_pthread_atfork != pthread_atfork,
|
||||
"Found pthread_atfork from libc");
|
||||
|
||||
if (notReadyCount > 0) {
|
||||
for (int i = 0; i < notReadyCount; ++i) {
|
||||
real_pthread_atfork(mozPthreadHandlers.handlers[i][idxPreFork],
|
||||
mozPthreadHandlers.handlers[i][idxPostForkParent],
|
||||
mozPthreadHandlers.handlers[i][idxPostForkChild]);
|
||||
}
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
void run_moz_pthread_atfork_handlers(struct moz_pthread_atfork_handler* list,
|
||||
int handlerIdx, bool reverse) {
|
||||
MOZ_ASSERT(list, "moz_pthread_atfork_handler should not be nullptr");
|
||||
for (int i = (reverse ? maxHandlers - 1 : 0);
|
||||
(reverse ? (i >= 0) : (i < maxHandlers)); (reverse ? --i : ++i)) {
|
||||
if (list->dsos[i]) {
|
||||
if (list->handlers[i][handlerIdx]) {
|
||||
(*list->handlers[i][handlerIdx])();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void run_moz_pthread_atfork_handlers_prefork() {
|
||||
run_moz_pthread_atfork_handlers(&mozPthreadHandlers, idxPreFork, true);
|
||||
}
|
||||
|
||||
void run_moz_pthread_atfork_handlers_postfork_parent() {
|
||||
run_moz_pthread_atfork_handlers(&mozPthreadHandlers, idxPostForkParent,
|
||||
false);
|
||||
}
|
||||
|
||||
void run_moz_pthread_atfork_handlers_postfork_child() {
|
||||
run_moz_pthread_atfork_handlers(&mozPthreadHandlers, idxPostForkChild, false);
|
||||
}
|
||||
#endif // defined(MOZ_ENABLE_FORKSERVER)
|
@ -45,6 +45,8 @@
|
||||
#include "sandbox/linux/system_headers/linux_syscalls.h"
|
||||
#include "sandbox/linux/services/syscall_wrappers.h"
|
||||
|
||||
#include "mozilla/pthread_atfork.h"
|
||||
|
||||
#ifdef MOZ_X11
|
||||
# ifndef MOZ_WIDGET_GTK
|
||||
# error "Unknown toolkit"
|
||||
@ -623,11 +625,14 @@ pid_t SandboxLaunch::Fork() {
|
||||
// can't run atfork hooks.)
|
||||
sigset_t oldSigs;
|
||||
BlockAllSignals(&oldSigs);
|
||||
run_moz_pthread_atfork_handlers_prefork();
|
||||
pid_t pid = ForkWithFlags(mFlags);
|
||||
if (pid != 0) {
|
||||
run_moz_pthread_atfork_handlers_postfork_parent();
|
||||
RestoreSignals(&oldSigs);
|
||||
return pid;
|
||||
}
|
||||
run_moz_pthread_atfork_handlers_postfork_child();
|
||||
|
||||
// WARNING: all code from this point on (and in StartChrootServer)
|
||||
// must be async signal safe. In particular, it cannot do anything
|
||||
|
Loading…
x
Reference in New Issue
Block a user