Loading mozglue/linker/CustomElf.cpp +9 −1 Original line number Diff line number Diff line Loading @@ -84,7 +84,10 @@ public: : GenericMappedPtr<Mappable1stPagePtr>( mappable->mmap(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, 0), PAGE_SIZE) , mappable(mappable) { } { /* Ensure the content of this page */ mappable->ensure(*this); } private: friend class GenericMappedPtr<Mappable1stPagePtr>; Loading Loading @@ -300,6 +303,11 @@ CustomElf::GetSymbolPtrInDeps(const char *symbol) const return FunctionPtr(&ElfLoader::__wrap_cxa_finalize); if (strcmp(symbol + 2, "dso_handle") == 0) return const_cast<CustomElf *>(this); } else if (symbol[0] == 's' && symbol[1] == 'i') { if (strcmp(symbol + 2, "gnal") == 0) return FunctionPtr(__wrap_signal); if (strcmp(symbol + 2, "gaction") == 0) return FunctionPtr(__wrap_sigaction); } void *sym; Loading mozglue/linker/CustomElf.h +1 −0 Original line number Diff line number Diff line Loading @@ -211,6 +211,7 @@ class Mappable; class CustomElf: public LibHandle, private ElfLoader::link_map { friend class ElfLoader; friend class SEGVHandler; public: /** * Returns a new CustomElf using the given file descriptor to map ELF Loading mozglue/linker/ElfLoader.cpp +125 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,19 @@ #include "Mappable.h" #include "Logging.h" #if defined(ANDROID) && ANDROID_VERSION < 8 /* Android API < 8 doesn't provide sigaltstack */ #include <sys/syscall.h> extern "C" { inline int sigaltstack(const stack_t *ss, stack_t *oss) { return syscall(__NR_sigaltstack, ss, oss); } } /* extern "C" */ #endif using namespace mozilla; #ifndef PAGE_SIZE Loading Loading @@ -565,3 +578,115 @@ ElfLoader::r_debug::Remove(ElfLoader::link_map *map) r_state = RT_CONSISTENT; r_brk(); } SEGVHandler::SEGVHandler() { /* Setup an alternative stack if the already existing one is not big * enough, or if there is none. */ if (sigaltstack(NULL, &oldStack) == -1 || !oldStack.ss_sp || oldStack.ss_size < stackSize) { stackPtr.Assign(mmap(NULL, stackSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), stackSize); stack_t stack; stack.ss_sp = stackPtr; stack.ss_size = stackSize; stack.ss_flags = 0; sigaltstack(&stack, NULL); } /* Register our own handler, and store the already registered one in * SEGVHandler's struct sigaction member */ struct sigaction action; action.sa_sigaction = &SEGVHandler::handler; sigemptyset(&action.sa_mask); action.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK; action.sa_restorer = NULL; sigaction(SIGSEGV, &action, &this->action); } SEGVHandler::~SEGVHandler() { /* Restore alternative stack for signals */ sigaltstack(&oldStack, NULL); /* Restore original signal handler */ sigaction(SIGSEGV, &this->action, NULL); } /* TODO: "properly" handle signal masks and flags */ void SEGVHandler::handler(int signum, siginfo_t *info, void *context) { //ASSERT(signum == SIGSEGV); debug("Caught segmentation fault @%p", info->si_addr); /* Check whether we segfaulted in the address space of a CustomElf. We're * only expecting that to happen as an access error. */ if (info->si_code == SEGV_ACCERR) { /* We may segfault when running destructors in CustomElf::~CustomElf, so we * can't hold a RefPtr on the handle. */ LibHandle *handle = ElfLoader::Singleton.GetHandleByPtr(info->si_addr).drop(); if (handle && !handle->IsSystemElf()) { debug("Within the address space of a CustomElf"); CustomElf *elf = static_cast<CustomElf *>(static_cast<LibHandle *>(handle)); if (elf->mappable->ensure(info->si_addr)) return; } } /* Redispatch to the registered handler */ SEGVHandler &that = ElfLoader::Singleton; if (that.action.sa_flags & SA_SIGINFO) { debug("Redispatching to registered handler @%p", that.action.sa_sigaction); that.action.sa_sigaction(signum, info, context); } else if (that.action.sa_handler == SIG_DFL) { debug("Redispatching to default handler"); /* Reset the handler to the default one, and trigger it. */ sigaction(signum, &that.action, NULL); raise(signum); } else if (that.action.sa_handler != SIG_IGN) { debug("Redispatching to registered handler @%p", that.action.sa_handler); that.action.sa_handler(signum); } else { debug("Ignoring"); } } sighandler_t __wrap_signal(int signum, sighandler_t handler) { /* Use system signal() function for all but SIGSEGV signals. */ if (signum != SIGSEGV) return signal(signum, handler); SEGVHandler &that = ElfLoader::Singleton; union { sighandler_t signal; void (*sigaction)(int, siginfo_t *, void *); } oldHandler; /* Keep the previous handler to return its value */ if (that.action.sa_flags & SA_SIGINFO) { oldHandler.sigaction = that.action.sa_sigaction; } else { oldHandler.signal = that.action.sa_handler; } /* Set the new handler */ that.action.sa_handler = handler; that.action.sa_flags = 0; return oldHandler.signal; } int __wrap_sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) { /* Use system sigaction() function for all but SIGSEGV signals. */ if (signum != SIGSEGV) return sigaction(signum, act, oldact); SEGVHandler &that = ElfLoader::Singleton; if (oldact) *oldact = that.action; if (act) that.action = *act; return 0; } mozglue/linker/ElfLoader.h +54 −1 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ #include <vector> #include <dlfcn.h> #include <signal.h> #include "mozilla/RefPtr.h" #include "Zip.h" Loading @@ -28,6 +29,10 @@ extern "C" { } Dl_info; #endif int __wrap_dladdr(void *addr, Dl_info *info); sighandler_t __wrap_signal(int signum, sighandler_t handler); int __wrap_sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); } /** Loading Loading @@ -119,6 +124,7 @@ protected: */ friend class ElfLoader; friend class CustomElf; friend class SEGVHandler; virtual bool IsSystemElf() const { return false; } private: Loading Loading @@ -173,10 +179,57 @@ private: void *dlhandle; }; /** * The ElfLoader registers its own SIGSEGV handler to handle segmentation * faults within the address space of the loaded libraries. It however * allows a handler to be set for faults in other places, and redispatches * to the handler set through signal() or sigaction(). We assume no system * library loaded with system dlopen is going to call signal or sigaction * for SIGSEGV. */ class SEGVHandler { protected: SEGVHandler(); ~SEGVHandler(); private: friend sighandler_t __wrap_signal(int signum, sighandler_t handler); friend int __wrap_sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); /** * SIGSEGV handler registered with __wrap_signal or __wrap_sigaction. */ struct sigaction action; /** * ElfLoader SIGSEGV handler. */ static void handler(int signum, siginfo_t *info, void *context); /** * Size of the alternative stack. The printf family requires more than 8KB * of stack, and our signal handler may print a few things. */ static const size_t stackSize = 12 * 1024; /** * Alternative stack information used before initialization. */ stack_t oldStack; /** * Pointer to an alternative stack for signals. Only set if oldStack is * not set or not big enough. */ MappedPtr stackPtr; }; /** * Elf Loader class in charge of loading and bookkeeping libraries. */ class ElfLoader class ElfLoader: public SEGVHandler { public: /** Loading mozglue/linker/Mappable.h +7 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,13 @@ private: friend class Mappable1stPagePtr; public: /** * Ensures the availability of the memory pages for the page(s) containing * the given address. Returns whether the pages were successfully made * available. */ virtual bool ensure(const void *addr) { return true; } /** * Indicate to a Mappable instance that no further mmap is going to happen. */ Loading Loading
mozglue/linker/CustomElf.cpp +9 −1 Original line number Diff line number Diff line Loading @@ -84,7 +84,10 @@ public: : GenericMappedPtr<Mappable1stPagePtr>( mappable->mmap(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, 0), PAGE_SIZE) , mappable(mappable) { } { /* Ensure the content of this page */ mappable->ensure(*this); } private: friend class GenericMappedPtr<Mappable1stPagePtr>; Loading Loading @@ -300,6 +303,11 @@ CustomElf::GetSymbolPtrInDeps(const char *symbol) const return FunctionPtr(&ElfLoader::__wrap_cxa_finalize); if (strcmp(symbol + 2, "dso_handle") == 0) return const_cast<CustomElf *>(this); } else if (symbol[0] == 's' && symbol[1] == 'i') { if (strcmp(symbol + 2, "gnal") == 0) return FunctionPtr(__wrap_signal); if (strcmp(symbol + 2, "gaction") == 0) return FunctionPtr(__wrap_sigaction); } void *sym; Loading
mozglue/linker/CustomElf.h +1 −0 Original line number Diff line number Diff line Loading @@ -211,6 +211,7 @@ class Mappable; class CustomElf: public LibHandle, private ElfLoader::link_map { friend class ElfLoader; friend class SEGVHandler; public: /** * Returns a new CustomElf using the given file descriptor to map ELF Loading
mozglue/linker/ElfLoader.cpp +125 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,19 @@ #include "Mappable.h" #include "Logging.h" #if defined(ANDROID) && ANDROID_VERSION < 8 /* Android API < 8 doesn't provide sigaltstack */ #include <sys/syscall.h> extern "C" { inline int sigaltstack(const stack_t *ss, stack_t *oss) { return syscall(__NR_sigaltstack, ss, oss); } } /* extern "C" */ #endif using namespace mozilla; #ifndef PAGE_SIZE Loading Loading @@ -565,3 +578,115 @@ ElfLoader::r_debug::Remove(ElfLoader::link_map *map) r_state = RT_CONSISTENT; r_brk(); } SEGVHandler::SEGVHandler() { /* Setup an alternative stack if the already existing one is not big * enough, or if there is none. */ if (sigaltstack(NULL, &oldStack) == -1 || !oldStack.ss_sp || oldStack.ss_size < stackSize) { stackPtr.Assign(mmap(NULL, stackSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), stackSize); stack_t stack; stack.ss_sp = stackPtr; stack.ss_size = stackSize; stack.ss_flags = 0; sigaltstack(&stack, NULL); } /* Register our own handler, and store the already registered one in * SEGVHandler's struct sigaction member */ struct sigaction action; action.sa_sigaction = &SEGVHandler::handler; sigemptyset(&action.sa_mask); action.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK; action.sa_restorer = NULL; sigaction(SIGSEGV, &action, &this->action); } SEGVHandler::~SEGVHandler() { /* Restore alternative stack for signals */ sigaltstack(&oldStack, NULL); /* Restore original signal handler */ sigaction(SIGSEGV, &this->action, NULL); } /* TODO: "properly" handle signal masks and flags */ void SEGVHandler::handler(int signum, siginfo_t *info, void *context) { //ASSERT(signum == SIGSEGV); debug("Caught segmentation fault @%p", info->si_addr); /* Check whether we segfaulted in the address space of a CustomElf. We're * only expecting that to happen as an access error. */ if (info->si_code == SEGV_ACCERR) { /* We may segfault when running destructors in CustomElf::~CustomElf, so we * can't hold a RefPtr on the handle. */ LibHandle *handle = ElfLoader::Singleton.GetHandleByPtr(info->si_addr).drop(); if (handle && !handle->IsSystemElf()) { debug("Within the address space of a CustomElf"); CustomElf *elf = static_cast<CustomElf *>(static_cast<LibHandle *>(handle)); if (elf->mappable->ensure(info->si_addr)) return; } } /* Redispatch to the registered handler */ SEGVHandler &that = ElfLoader::Singleton; if (that.action.sa_flags & SA_SIGINFO) { debug("Redispatching to registered handler @%p", that.action.sa_sigaction); that.action.sa_sigaction(signum, info, context); } else if (that.action.sa_handler == SIG_DFL) { debug("Redispatching to default handler"); /* Reset the handler to the default one, and trigger it. */ sigaction(signum, &that.action, NULL); raise(signum); } else if (that.action.sa_handler != SIG_IGN) { debug("Redispatching to registered handler @%p", that.action.sa_handler); that.action.sa_handler(signum); } else { debug("Ignoring"); } } sighandler_t __wrap_signal(int signum, sighandler_t handler) { /* Use system signal() function for all but SIGSEGV signals. */ if (signum != SIGSEGV) return signal(signum, handler); SEGVHandler &that = ElfLoader::Singleton; union { sighandler_t signal; void (*sigaction)(int, siginfo_t *, void *); } oldHandler; /* Keep the previous handler to return its value */ if (that.action.sa_flags & SA_SIGINFO) { oldHandler.sigaction = that.action.sa_sigaction; } else { oldHandler.signal = that.action.sa_handler; } /* Set the new handler */ that.action.sa_handler = handler; that.action.sa_flags = 0; return oldHandler.signal; } int __wrap_sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) { /* Use system sigaction() function for all but SIGSEGV signals. */ if (signum != SIGSEGV) return sigaction(signum, act, oldact); SEGVHandler &that = ElfLoader::Singleton; if (oldact) *oldact = that.action; if (act) that.action = *act; return 0; }
mozglue/linker/ElfLoader.h +54 −1 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ #include <vector> #include <dlfcn.h> #include <signal.h> #include "mozilla/RefPtr.h" #include "Zip.h" Loading @@ -28,6 +29,10 @@ extern "C" { } Dl_info; #endif int __wrap_dladdr(void *addr, Dl_info *info); sighandler_t __wrap_signal(int signum, sighandler_t handler); int __wrap_sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); } /** Loading Loading @@ -119,6 +124,7 @@ protected: */ friend class ElfLoader; friend class CustomElf; friend class SEGVHandler; virtual bool IsSystemElf() const { return false; } private: Loading Loading @@ -173,10 +179,57 @@ private: void *dlhandle; }; /** * The ElfLoader registers its own SIGSEGV handler to handle segmentation * faults within the address space of the loaded libraries. It however * allows a handler to be set for faults in other places, and redispatches * to the handler set through signal() or sigaction(). We assume no system * library loaded with system dlopen is going to call signal or sigaction * for SIGSEGV. */ class SEGVHandler { protected: SEGVHandler(); ~SEGVHandler(); private: friend sighandler_t __wrap_signal(int signum, sighandler_t handler); friend int __wrap_sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); /** * SIGSEGV handler registered with __wrap_signal or __wrap_sigaction. */ struct sigaction action; /** * ElfLoader SIGSEGV handler. */ static void handler(int signum, siginfo_t *info, void *context); /** * Size of the alternative stack. The printf family requires more than 8KB * of stack, and our signal handler may print a few things. */ static const size_t stackSize = 12 * 1024; /** * Alternative stack information used before initialization. */ stack_t oldStack; /** * Pointer to an alternative stack for signals. Only set if oldStack is * not set or not big enough. */ MappedPtr stackPtr; }; /** * Elf Loader class in charge of loading and bookkeeping libraries. */ class ElfLoader class ElfLoader: public SEGVHandler { public: /** Loading
mozglue/linker/Mappable.h +7 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,13 @@ private: friend class Mappable1stPagePtr; public: /** * Ensures the availability of the memory pages for the page(s) containing * the given address. Returns whether the pages were successfully made * available. */ virtual bool ensure(const void *addr) { return true; } /** * Indicate to a Mappable instance that no further mmap is going to happen. */ Loading