summaryrefslogtreecommitdiffstats
path: root/mozglue/interposers
diff options
context:
space:
mode:
Diffstat (limited to 'mozglue/interposers')
-rw-r--r--mozglue/interposers/InterposerHelper.h79
-rw-r--r--mozglue/interposers/env_interposer.cpp79
-rw-r--r--mozglue/interposers/getline_interposer.cpp110
-rw-r--r--mozglue/interposers/moz.build25
-rw-r--r--mozglue/interposers/pthread_create_interposer.cpp108
5 files changed, 401 insertions, 0 deletions
diff --git a/mozglue/interposers/InterposerHelper.h b/mozglue/interposers/InterposerHelper.h
new file mode 100644
index 0000000000..58e55d081e
--- /dev/null
+++ b/mozglue/interposers/InterposerHelper.h
@@ -0,0 +1,79 @@
+/* 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 InterposerHelper_h
+#define InterposerHelper_h
+
+#include <type_traits>
+
+#ifdef MOZ_LINKER
+# include "Linker.h"
+#else
+# include <dlfcn.h>
+#endif
+
+#include "mozilla/Assertions.h"
+
+template <typename T>
+static inline T dlsym_wrapper(void* aHandle, const char* aName) {
+#ifdef MOZ_LINKER
+ return reinterpret_cast<T>(__wrap_dlsym(aHandle, aName));
+#else
+ return reinterpret_cast<T>(dlsym(aHandle, aName));
+#endif // MOZ_LINKER
+}
+
+static inline void* dlopen_wrapper(const char* aPath, int flags) {
+#ifdef MOZ_LINKER
+ return __wrap_dlopen(aPath, flags);
+#else
+ return dlopen(aPath, flags);
+#endif // MOZ_LINKER
+}
+
+template <typename T>
+static T get_real_symbol(const char* aName, T aReplacementSymbol) {
+ // T can only be a function pointer
+ static_assert(std::is_function<typename std::remove_pointer<T>::type>::value);
+
+ // Find the corresponding function in the linked libraries
+ T real_symbol = dlsym_wrapper<T>(RTLD_NEXT, aName);
+
+#if defined(ANDROID)
+ if ((real_symbol == nullptr) || (real_symbol == aReplacementSymbol)) {
+ // On old versions of Android the application runtime links in libc before
+ // we get a chance to link libmozglue, so its symbols don't appear when
+ // resolving them with RTLD_NEXT. This behavior differs between the
+ // different versions of Android so we'll just look for them directly into
+ // libc.so. Note that this won't work if we're trying to interpose
+ // functions that are in other libraries, but hopefully we'll never have
+ // to do that.
+ void* handle = dlopen_wrapper("libc.so", RTLD_LAZY);
+
+ if (handle) {
+ real_symbol = dlsym_wrapper<T>(handle, aName);
+ }
+ }
+#endif
+
+ if (real_symbol == nullptr) {
+ MOZ_CRASH_UNSAFE_PRINTF(
+ "%s() interposition failed but the interposer function is "
+ "still being called, this won't work!",
+ aName);
+ }
+
+ if (real_symbol == aReplacementSymbol) {
+ MOZ_CRASH_UNSAFE_PRINTF(
+ "We could not obtain the real %s(). Calling the symbol we "
+ "got would make us enter an infinite loop so stop here instead.",
+ aName);
+ }
+
+ return real_symbol;
+}
+
+#define GET_REAL_SYMBOL(name) get_real_symbol(#name, name)
+
+#endif // InterposerHelper_h
diff --git a/mozglue/interposers/env_interposer.cpp b/mozglue/interposers/env_interposer.cpp
new file mode 100644
index 0000000000..d8c11b5d35
--- /dev/null
+++ b/mozglue/interposers/env_interposer.cpp
@@ -0,0 +1,79 @@
+/* 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 <pthread.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "InterposerHelper.h"
+
+// The interposers in this file cover all the functions used to access the
+// environment (getenv(), putenv(), setenv(), unsetenv() and clearenv()). They
+// all use the mutex below for synchronization to prevent races that caused
+// startup crashes, see bug 1752703.
+static pthread_mutex_t gEnvLock = PTHREAD_MUTEX_INITIALIZER;
+
+static char* internal_getenv(const char* aName) {
+ if (environ == nullptr || aName[0] == '\0') {
+ return nullptr;
+ }
+
+ size_t len = strlen(aName);
+ for (char** env_ptr = environ; *env_ptr != nullptr; ++env_ptr) {
+ if ((aName[0] == (*env_ptr)[0]) && (strncmp(aName, *env_ptr, len) == 0) &&
+ ((*env_ptr)[len] == '=')) {
+ return *env_ptr + len + 1;
+ }
+ }
+
+ return nullptr;
+}
+
+extern "C" {
+
+MFBT_API char* getenv(const char* name) {
+ pthread_mutex_lock(&gEnvLock);
+ char* result = internal_getenv(name);
+ pthread_mutex_unlock(&gEnvLock);
+
+ return result;
+}
+
+MFBT_API int putenv(char* string) {
+ static const auto real_putenv = GET_REAL_SYMBOL(putenv);
+
+ pthread_mutex_lock(&gEnvLock);
+ int result = real_putenv(string);
+ pthread_mutex_unlock(&gEnvLock);
+ return result;
+}
+
+MFBT_API int setenv(const char* name, const char* value, int replace) {
+ static const auto real_setenv = GET_REAL_SYMBOL(setenv);
+
+ pthread_mutex_lock(&gEnvLock);
+ int result = real_setenv(name, value, replace);
+ pthread_mutex_unlock(&gEnvLock);
+ return result;
+}
+
+MFBT_API int unsetenv(const char* name) {
+ static const auto real_unsetenv = GET_REAL_SYMBOL(unsetenv);
+
+ pthread_mutex_lock(&gEnvLock);
+ int result = real_unsetenv(name);
+ pthread_mutex_unlock(&gEnvLock);
+ return result;
+}
+
+MFBT_API int clearenv(void) {
+ static const auto real_clearenv = GET_REAL_SYMBOL(clearenv);
+
+ pthread_mutex_lock(&gEnvLock);
+ int result = real_clearenv();
+ pthread_mutex_unlock(&gEnvLock);
+ return result;
+}
+
+} // extern "C"
diff --git a/mozglue/interposers/getline_interposer.cpp b/mozglue/interposers/getline_interposer.cpp
new file mode 100644
index 0000000000..902b52b718
--- /dev/null
+++ b/mozglue/interposers/getline_interposer.cpp
@@ -0,0 +1,110 @@
+/* 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/. */
+
+/*
+ * Interposing getline because of
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=914190
+ */
+
+#ifdef __ANDROID__
+
+# include <cstdlib>
+# include <cstdio>
+# include <cerrno>
+
+# include <limits>
+
+namespace {
+
+// RAII on file locking.
+class FileLocker {
+ FILE* stream;
+
+ public:
+ explicit FileLocker(FILE* stream) : stream(stream) { flockfile(stream); }
+ ~FileLocker() { funlockfile(stream); }
+};
+
+ssize_t internal_getdelim(char** __restrict lineptr, size_t* __restrict n,
+ int delim, FILE* __restrict stream) {
+ constexpr size_t n_default = 64;
+ constexpr size_t n_max =
+ std::numeric_limits<ssize_t>::max() < std::numeric_limits<size_t>::max()
+ ? (size_t)std::numeric_limits<ssize_t>::max() + 1
+ : std::numeric_limits<size_t>::max();
+ constexpr size_t n_limit = 2 * ((n_max - 1) / 3);
+
+ if (!lineptr || !n || !stream) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ // Lock the file so that we can us unlocked getc in the inner loop.
+ FileLocker fl(stream);
+
+ if (!*lineptr || *n == 0) {
+ *n = n_default;
+ if (auto* new_lineptr = reinterpret_cast<char*>(realloc(*lineptr, *n))) {
+ *lineptr = new_lineptr;
+ } else {
+ errno = ENOMEM;
+ return -1;
+ }
+ }
+
+ ssize_t result;
+ size_t cur_len = 0;
+
+ while (true) {
+ // Retrieve an extra char.
+ int i = getc_unlocked(stream);
+ if (i == EOF) {
+ result = -1;
+ break;
+ }
+
+ // Eventually grow the buffer.
+ if (cur_len + 1 >= *n) {
+ size_t needed = *n >= n_limit ? n_max : 3 * *n / 2 + 1;
+
+ if (cur_len + 1 >= needed) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ if (auto* new_lineptr = (char*)realloc(*lineptr, needed)) {
+ *lineptr = new_lineptr;
+ } else {
+ errno = ENOMEM;
+ return -1;
+ }
+ *n = needed;
+ }
+
+ (*lineptr)[cur_len] = i;
+ cur_len++;
+
+ if (i == delim) break;
+ }
+ (*lineptr)[cur_len] = '\0';
+ return cur_len ? cur_len : result;
+}
+
+} // namespace
+
+extern "C" {
+
+MFBT_API ssize_t getline(char** __restrict lineptr, size_t* __restrict n,
+ FILE* __restrict stream) {
+ return internal_getdelim(lineptr, n, '\n', stream);
+}
+
+MFBT_API ssize_t getdelim(char** __restrict lineptr, size_t* __restrict n,
+ int delim, FILE* __restrict stream) {
+ return internal_getdelim(lineptr, n, delim, stream);
+}
+
+} // extern "C"
+
+#endif
diff --git a/mozglue/interposers/moz.build b/mozglue/interposers/moz.build
new file mode 100644
index 0000000000..48e88946b5
--- /dev/null
+++ b/mozglue/interposers/moz.build
@@ -0,0 +1,25 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+Library("interposers")
+
+DEFINES["IMPL_MFBT"] = True
+
+UNIFIED_SOURCES += [
+ "env_interposer.cpp",
+ "getline_interposer.cpp",
+]
+
+if CONFIG["MOZ_CRASHREPORTER"]:
+ UNIFIED_SOURCES += [
+ "pthread_create_interposer.cpp",
+ ]
+
+if CONFIG["MOZ_LINKER"]:
+ LOCAL_INCLUDES += [
+ "/mozglue/linker",
+ ]
+
+FINAL_LIBRARY = "mozglue"
diff --git a/mozglue/interposers/pthread_create_interposer.cpp b/mozglue/interposers/pthread_create_interposer.cpp
new file mode 100644
index 0000000000..65f60c2d1b
--- /dev/null
+++ b/mozglue/interposers/pthread_create_interposer.cpp
@@ -0,0 +1,108 @@
+/* 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 <algorithm>
+
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+
+#include "InterposerHelper.h"
+
+using mozilla::DebugOnly;
+
+struct SigAltStack {
+ void* mem;
+ size_t size;
+};
+
+struct PthreadCreateParams {
+ void* (*start_routine)(void*);
+ void* arg;
+};
+
+// Install the alternate signal stack, returns a pointer to the memory area we
+// mapped to store the stack only if it was installed successfully, otherwise
+// returns NULL.
+static void* install_sig_alt_stack(size_t size) {
+ void* alt_stack_mem = mmap(nullptr, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (alt_stack_mem) {
+ stack_t alt_stack = {
+ .ss_sp = alt_stack_mem,
+ .ss_flags = 0,
+ .ss_size = size,
+ };
+
+ int rv = sigaltstack(&alt_stack, nullptr);
+ if (rv == 0) {
+ return alt_stack_mem;
+ }
+
+ rv = munmap(alt_stack_mem, size);
+ MOZ_ASSERT(rv == 0);
+ }
+
+ return nullptr;
+}
+
+// Uninstall the alternate signal handler and unmaps it. Does nothing if
+// alt_stack_mem is NULL.
+static void uninstall_sig_alt_stack(void* alt_stack_ptr) {
+ SigAltStack* alt_stack = static_cast<SigAltStack*>(alt_stack_ptr);
+ if (alt_stack->mem) {
+ stack_t disable_alt_stack = {};
+ disable_alt_stack.ss_flags = SS_DISABLE;
+ DebugOnly<int> rv = sigaltstack(&disable_alt_stack, nullptr);
+ MOZ_ASSERT(rv == 0);
+ rv = munmap(alt_stack->mem, alt_stack->size);
+ MOZ_ASSERT(rv == 0);
+ }
+}
+
+// This replaces the routine passed to pthread_create() when a thread is
+// started, it handles the alternate signal stack and calls the thread's
+// actual routine.
+void* set_alt_signal_stack_and_start(PthreadCreateParams* params) {
+ void* (*start_routine)(void*) = params->start_routine;
+ void* arg = params->arg;
+ free(params);
+
+ void* thread_rv = nullptr;
+ static const size_t kSigStackSize = std::max(size_t(16384), size_t(SIGSTKSZ));
+ void* alt_stack_mem = install_sig_alt_stack(kSigStackSize);
+ SigAltStack alt_stack{alt_stack_mem, kSigStackSize};
+ pthread_cleanup_push(uninstall_sig_alt_stack, &alt_stack);
+ thread_rv = start_routine(arg);
+ pthread_cleanup_pop(1);
+
+ return thread_rv;
+}
+
+extern "C" {
+// This interposer replaces libpthread's pthread_create() so that we can
+// inject an alternate signal stack in every new thread.
+MFBT_API int pthread_create(pthread_t* thread, const pthread_attr_t* attr,
+ void* (*start_routine)(void*), void* arg) {
+ static const auto real_pthread_create = GET_REAL_SYMBOL(pthread_create);
+
+ PthreadCreateParams* params =
+ (PthreadCreateParams*)malloc(sizeof(PthreadCreateParams));
+ params->start_routine = start_routine;
+ params->arg = arg;
+
+ int result = real_pthread_create(
+ thread, attr, (void* (*)(void*))set_alt_signal_stack_and_start, params);
+
+ if (result != 0) {
+ free(params);
+ }
+
+ return result;
+}
+}