зеркало из https://github.com/mozilla/gecko-dev.git
Bug 874708 - Hook libc's sigaction to avoid system libraries replacing our segfault handler temporarily and restoring it wrongly. r=nfroyd
This commit is contained in:
Родитель
800328cc02
Коммит
7a3c231b71
|
@ -308,11 +308,6 @@ CustomElf::GetSymbolPtrInDeps(const char *symbol) const
|
|||
return const_cast<CustomElf *>(this);
|
||||
if (strcmp(symbol + 2, "moz_linker_stats") == 0)
|
||||
return FunctionPtr(&ElfLoader::stats);
|
||||
} 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;
|
||||
|
@ -429,9 +424,11 @@ CustomElf::LoadSegment(const Phdr *pt_load) const
|
|||
}
|
||||
|
||||
/* Ensure the availability of all pages within the mapping if on-demand
|
||||
* decompression is disabled (MOZ_LINKER_ONDEMAND=0). */
|
||||
* decompression is disabled (MOZ_LINKER_ONDEMAND=0 or signal handler not
|
||||
* registered). */
|
||||
const char *ondemand = getenv("MOZ_LINKER_ONDEMAND");
|
||||
if (ondemand && !strncmp(ondemand, "0", 2 /* Including '\0' */)) {
|
||||
if (!ElfLoader::Singleton.hasRegisteredHandler() ||
|
||||
(ondemand && !strncmp(ondemand, "0", 2 /* Including '\0' */))) {
|
||||
for (Addr off = 0; off < pt_load->p_filesz; off += PAGE_SIZE) {
|
||||
mappable->ensure(reinterpret_cast<char *>(mapped) + off);
|
||||
}
|
||||
|
|
|
@ -16,10 +16,11 @@
|
|||
#include "Logging.h"
|
||||
|
||||
#if defined(ANDROID)
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include <android/api-level.h>
|
||||
#if __ANDROID_API__ < 8
|
||||
/* Android API < 8 doesn't provide sigaltstack */
|
||||
#include <sys/syscall.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
|
@ -674,26 +675,38 @@ class EnsureWritable
|
|||
{
|
||||
public:
|
||||
template <typename T>
|
||||
EnsureWritable(T *&ptr)
|
||||
EnsureWritable(T *ptr, size_t length_ = sizeof(T))
|
||||
{
|
||||
prot = getProt((uintptr_t) &ptr);
|
||||
if (prot == -1)
|
||||
MOZ_ASSERT(length_ < PAGE_SIZE);
|
||||
prot = -1;
|
||||
page = MAP_FAILED;
|
||||
|
||||
uintptr_t firstPage = reinterpret_cast<uintptr_t>(ptr) & PAGE_MASK;
|
||||
length = (reinterpret_cast<uintptr_t>(ptr) + length_ - firstPage
|
||||
+ PAGE_SIZE - 1) & PAGE_MASK;
|
||||
|
||||
uintptr_t end;
|
||||
|
||||
prot = getProt(firstPage, &end);
|
||||
if (prot == -1 || (firstPage + length) > end)
|
||||
MOZ_CRASH();
|
||||
/* Pointers are aligned such that their value can't be spanning across
|
||||
* 2 pages. */
|
||||
page = (void*)((uintptr_t) &ptr & PAGE_MASK);
|
||||
if (!(prot & PROT_WRITE))
|
||||
mprotect(page, PAGE_SIZE, prot | PROT_WRITE);
|
||||
|
||||
if (prot & PROT_WRITE)
|
||||
return;
|
||||
|
||||
page = reinterpret_cast<void *>(firstPage);
|
||||
mprotect(page, length, prot | PROT_WRITE);
|
||||
}
|
||||
|
||||
~EnsureWritable()
|
||||
{
|
||||
if (!(prot & PROT_WRITE))
|
||||
mprotect(page, PAGE_SIZE, prot);
|
||||
if (page != MAP_FAILED) {
|
||||
mprotect(page, length, prot);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int getProt(uintptr_t addr)
|
||||
int getProt(uintptr_t addr, uintptr_t *end)
|
||||
{
|
||||
/* The interesting part of the /proc/self/maps format looks like:
|
||||
* startAddr-endAddr rwxp */
|
||||
|
@ -718,6 +731,7 @@ private:
|
|||
result |= PROT_EXEC;
|
||||
else if (perms[2] != '-')
|
||||
return -1;
|
||||
*end = endAddr;
|
||||
return result;
|
||||
}
|
||||
return -1;
|
||||
|
@ -725,6 +739,7 @@ private:
|
|||
|
||||
int prot;
|
||||
void *page;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -756,7 +771,7 @@ ElfLoader::DebuggerHelper::Add(ElfLoader::link_map *map)
|
|||
firstAdded = map;
|
||||
/* When adding a library for the first time, r_map points to data
|
||||
* handled by the system linker, and that data may be read-only */
|
||||
EnsureWritable w(dbg->r_map->l_prev);
|
||||
EnsureWritable w(&dbg->r_map->l_prev);
|
||||
dbg->r_map->l_prev = map;
|
||||
} else
|
||||
dbg->r_map->l_prev = map;
|
||||
|
@ -780,7 +795,7 @@ ElfLoader::DebuggerHelper::Remove(ElfLoader::link_map *map)
|
|||
firstAdded = map->l_prev;
|
||||
/* When removing the first added library, its l_next is going to be
|
||||
* data handled by the system linker, and that data may be read-only */
|
||||
EnsureWritable w(map->l_next->l_prev);
|
||||
EnsureWritable w(&map->l_next->l_prev);
|
||||
map->l_next->l_prev = map->l_prev;
|
||||
} else
|
||||
map->l_next->l_prev = map->l_prev;
|
||||
|
@ -788,8 +803,102 @@ ElfLoader::DebuggerHelper::Remove(ElfLoader::link_map *map)
|
|||
dbg->r_brk();
|
||||
}
|
||||
|
||||
SEGVHandler::SEGVHandler()
|
||||
#if defined(ANDROID)
|
||||
/* As some system libraries may be calling signal() or sigaction() to
|
||||
* set a SIGSEGV handler, effectively breaking MappableSeekableZStream,
|
||||
* or worse, restore our SIGSEGV handler with wrong flags (which using
|
||||
* signal() will do), we want to hook into the system's sigaction() to
|
||||
* replace it with our own wrapper instead, so that our handler is never
|
||||
* replaced. We used to only do that with libraries this linker loads,
|
||||
* but it turns out at least one system library does call signal() and
|
||||
* breaks us (libsc-a3xx.so on the Samsung Galaxy S4).
|
||||
* As libc's signal (bsd_signal/sysv_signal, really) calls sigaction
|
||||
* under the hood, instead of calling the signal system call directly,
|
||||
* we only need to hook sigaction. This is true for both bionic and
|
||||
* glibc.
|
||||
*/
|
||||
|
||||
/* libc's sigaction */
|
||||
extern "C" int
|
||||
sigaction(int signum, const struct sigaction *act,
|
||||
struct sigaction *oldact);
|
||||
|
||||
/* Simple reimplementation of sigaction. This is roughly equivalent
|
||||
* to the assembly that comes in bionic, but not quite equivalent to
|
||||
* glibc's implementation, so we only use this on Android. */
|
||||
int
|
||||
sys_sigaction(int signum, const struct sigaction *act,
|
||||
struct sigaction *oldact)
|
||||
{
|
||||
return syscall(__NR_sigaction, signum, act, oldact);
|
||||
}
|
||||
|
||||
/* Replace the first instructions of the given function with a jump
|
||||
* to the given new function. */
|
||||
template <typename T>
|
||||
static bool
|
||||
Divert(T func, T new_func)
|
||||
{
|
||||
void *ptr = FunctionPtr(func);
|
||||
uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
|
||||
|
||||
#if defined(__i386__)
|
||||
// A 32-bit jump is a 5 bytes instruction.
|
||||
EnsureWritable w(ptr, 5);
|
||||
*reinterpret_cast<unsigned char *>(addr) = 0xe9; // jmp
|
||||
*reinterpret_cast<intptr_t *>(addr + 1) =
|
||||
reinterpret_cast<uintptr_t>(new_func) - addr - 5; // target displacement
|
||||
return true;
|
||||
#elif defined(__arm__)
|
||||
const unsigned char trampoline[] = {
|
||||
// .thumb
|
||||
0x46, 0x04, // nop
|
||||
0x78, 0x47, // bx pc
|
||||
0x46, 0x04, // nop
|
||||
// .arm
|
||||
0x04, 0xf0, 0x1f, 0xe5, // ldr pc, [pc, #-4]
|
||||
// .word <new_func>
|
||||
};
|
||||
const unsigned char *start;
|
||||
if (addr & 0x01) {
|
||||
/* Function is thumb, the actual address of the code is without the
|
||||
* least significant bit. */
|
||||
addr--;
|
||||
/* The arm part of the trampoline needs to be 32-bit aligned */
|
||||
if (addr & 0x02)
|
||||
start = trampoline;
|
||||
else
|
||||
start = trampoline + 2;
|
||||
} else {
|
||||
/* Function is arm, we only need the arm part of the trampoline */
|
||||
start = trampoline + 6;
|
||||
}
|
||||
|
||||
size_t len = sizeof(trampoline) - (start - trampoline);
|
||||
EnsureWritable w(reinterpret_cast<void *>(addr), len + sizeof(void *));
|
||||
memcpy(reinterpret_cast<void *>(addr), start, len);
|
||||
*reinterpret_cast<void **>(addr + len) = FunctionPtr(new_func);
|
||||
cacheflush(addr, addr + len + sizeof(void *), 0);
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
#define sys_sigaction sigaction
|
||||
template <typename T>
|
||||
static bool
|
||||
Divert(T func, T new_func)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
SEGVHandler::SEGVHandler()
|
||||
: registeredHandler(false)
|
||||
{
|
||||
if (!Divert(sigaction, __wrap_sigaction))
|
||||
return;
|
||||
/* 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 ||
|
||||
|
@ -809,7 +918,7 @@ SEGVHandler::SEGVHandler()
|
|||
sigemptyset(&action.sa_mask);
|
||||
action.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
|
||||
action.sa_restorer = NULL;
|
||||
sigaction(SIGSEGV, &action, &this->action);
|
||||
registeredHandler = !sys_sigaction(SIGSEGV, &action, &this->action);
|
||||
}
|
||||
|
||||
SEGVHandler::~SEGVHandler()
|
||||
|
@ -817,7 +926,7 @@ SEGVHandler::~SEGVHandler()
|
|||
/* Restore alternative stack for signals */
|
||||
sigaltstack(&oldStack, NULL);
|
||||
/* Restore original signal handler */
|
||||
sigaction(SIGSEGV, &this->action, NULL);
|
||||
sys_sigaction(SIGSEGV, &this->action, NULL);
|
||||
}
|
||||
|
||||
/* TODO: "properly" handle signal masks and flags */
|
||||
|
@ -848,7 +957,7 @@ void SEGVHandler::handler(int signum, siginfo_t *info, void *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);
|
||||
sys_sigaction(signum, &that.action, NULL);
|
||||
raise(signum);
|
||||
} else if (that.action.sa_handler != SIG_IGN) {
|
||||
debug("Redispatching to registered handler @%p",
|
||||
|
@ -858,40 +967,14 @@ void SEGVHandler::handler(int signum, siginfo_t *info, void *context)
|
|||
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)
|
||||
SEGVHandler::__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);
|
||||
return sys_sigaction(signum, act, oldact);
|
||||
|
||||
SEGVHandler &that = ElfLoader::Singleton;
|
||||
if (oldact)
|
||||
|
|
|
@ -31,10 +31,6 @@ extern "C" {
|
|||
#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);
|
||||
|
||||
struct dl_phdr_info {
|
||||
Elf::Addr dlpi_addr;
|
||||
const char *dlpi_name;
|
||||
|
@ -292,19 +288,21 @@ private:
|
|||
* 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.
|
||||
* to the handler set through signal() or sigaction().
|
||||
*/
|
||||
class SEGVHandler
|
||||
{
|
||||
public:
|
||||
bool hasRegisteredHandler() {
|
||||
return registeredHandler;
|
||||
}
|
||||
|
||||
protected:
|
||||
SEGVHandler();
|
||||
~SEGVHandler();
|
||||
|
||||
private:
|
||||
friend sighandler_t __wrap_signal(int signum, sighandler_t handler);
|
||||
friend int __wrap_sigaction(int signum, const struct sigaction *act,
|
||||
static int __wrap_sigaction(int signum, const struct sigaction *act,
|
||||
struct sigaction *oldact);
|
||||
|
||||
/**
|
||||
|
@ -333,6 +331,8 @@ private:
|
|||
* not set or not big enough.
|
||||
*/
|
||||
MappedPtr stackPtr;
|
||||
|
||||
bool registeredHandler;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче