diff options
Diffstat (limited to 'mozglue/interposers/pthread_create_interposer.cpp')
-rw-r--r-- | mozglue/interposers/pthread_create_interposer.cpp | 108 |
1 files changed, 108 insertions, 0 deletions
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; +} +} |