diff options
author | Josh Gao <jmgao@google.com> | 2017-08-21 14:31:17 -0700 |
---|---|---|
committer | Josh Gao <jmgao@google.com> | 2017-12-15 14:11:12 -0800 |
commit | 2b2ae0c88ef83c4c53297ff54fa601b18c014fa4 (patch) | |
tree | 832d3ab764da9a63d4e22001f18045fa13078dd5 /debuggerd/libdebuggerd/utility.cpp | |
parent | 385ea22741ed5bad794fb6b1dff2b46481f241c4 (diff) |
crash_dump: fork a copy of the target's address space.
Reduce the amount of time that a process remains paused by pausing its
threads, fetching their registers, and then performing unwinding on a
copy of its address space. This also works around a kernel change
that's in 4.9 that prevents ptrace from reading memory of processes
that we don't have immediate permissions to ptrace (even if we
previously ptraced them).
Bug: http://b/62112103
Bug: http://b/63989615
Test: treehugger
Change-Id: I7b9cc5dd8f54a354bc61f1bda0d2b7a8a55733c4
Diffstat (limited to 'debuggerd/libdebuggerd/utility.cpp')
-rw-r--r-- | debuggerd/libdebuggerd/utility.cpp | 202 |
1 files changed, 174 insertions, 28 deletions
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp index 1b746527e..247d806ba 100644 --- a/debuggerd/libdebuggerd/utility.cpp +++ b/debuggerd/libdebuggerd/utility.cpp @@ -21,6 +21,8 @@ #include <errno.h> #include <signal.h> #include <string.h> +#include <sys/capability.h> +#include <sys/prctl.h> #include <sys/ptrace.h> #include <sys/uio.h> #include <sys/wait.h> @@ -34,7 +36,9 @@ #include <android-base/strings.h> #include <android-base/unique_fd.h> #include <backtrace/Backtrace.h> +#include <debuggerd/handler.h> #include <log/log.h> +#include <unwindstack/Memory.h> using android::base::unique_fd; @@ -117,34 +121,10 @@ void _LOG(log_t* log, enum logtype ltype, const char* fmt, ...) { } } -bool wait_for_signal(pid_t tid, siginfo_t* siginfo) { - while (true) { - int status; - pid_t n = TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL)); - if (n == -1) { - ALOGE("waitpid failed: tid %d, %s", tid, strerror(errno)); - return false; - } else if (n == tid) { - if (WIFSTOPPED(status)) { - if (ptrace(PTRACE_GETSIGINFO, tid, nullptr, siginfo) != 0) { - ALOGE("PTRACE_GETSIGINFO failed: %s", strerror(errno)); - return false; - } - return true; - } else { - ALOGE("unexpected waitpid response: n=%d, status=%08x\n", n, status); - // This is the only circumstance under which we can allow a detach - // to fail with ESRCH, which indicates the tid has exited. - return false; - } - } - } -} - #define MEMORY_BYTES_TO_DUMP 256 #define MEMORY_BYTES_PER_LINE 16 -void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...) { +void dump_memory(log_t* log, unwindstack::Memory* memory, uintptr_t addr, const char* fmt, ...) { std::string log_msg; va_list ap; va_start(ap, fmt); @@ -172,7 +152,7 @@ void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* f // Dump 256 bytes uintptr_t data[MEMORY_BYTES_TO_DUMP/sizeof(uintptr_t)]; memset(data, 0, MEMORY_BYTES_TO_DUMP); - size_t bytes = backtrace->Read(addr, reinterpret_cast<uint8_t*>(data), sizeof(data)); + size_t bytes = memory->Read(addr, reinterpret_cast<uint8_t*>(data), sizeof(data)); if (bytes % sizeof(uintptr_t) != 0) { // This should never happen, but just in case. ALOGE("Bytes read %zu, is not a multiple of %zu", bytes, sizeof(uintptr_t)); @@ -199,8 +179,8 @@ void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* f // into a readable map. Only requires one extra read because a map has // to contain at least one page, and the total number of bytes to dump // is smaller than a page. - size_t bytes2 = backtrace->Read(addr + start + bytes, reinterpret_cast<uint8_t*>(data) + bytes, - sizeof(data) - bytes - start); + size_t bytes2 = memory->Read(addr + start + bytes, reinterpret_cast<uint8_t*>(data) + bytes, + sizeof(data) - bytes - start); bytes += bytes2; if (bytes2 > 0 && bytes % sizeof(uintptr_t) != 0) { // This should never happen, but we'll try and continue any way. @@ -264,3 +244,169 @@ void read_with_default(const char* path, char* buf, size_t len, const char* defa } strcpy(buf, default_value); } + +void drop_capabilities() { + __user_cap_header_struct capheader; + memset(&capheader, 0, sizeof(capheader)); + capheader.version = _LINUX_CAPABILITY_VERSION_3; + capheader.pid = 0; + + __user_cap_data_struct capdata[2]; + memset(&capdata, 0, sizeof(capdata)); + + if (capset(&capheader, &capdata[0]) == -1) { + PLOG(FATAL) << "failed to drop capabilities"; + } + + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) { + PLOG(FATAL) << "failed to set PR_SET_NO_NEW_PRIVS"; + } +} + +bool signal_has_si_addr(int si_signo, int si_code) { + // Manually sent signals won't have si_addr. + if (si_code == SI_USER || si_code == SI_QUEUE || si_code == SI_TKILL) { + return false; + } + + switch (si_signo) { + case SIGBUS: + case SIGFPE: + case SIGILL: + case SIGSEGV: + case SIGTRAP: + return true; + default: + return false; + } +} + +const char* get_signame(int sig) { + switch (sig) { + case SIGABRT: return "SIGABRT"; + case SIGBUS: return "SIGBUS"; + case SIGFPE: return "SIGFPE"; + case SIGILL: return "SIGILL"; + case SIGSEGV: return "SIGSEGV"; +#if defined(SIGSTKFLT) + case SIGSTKFLT: return "SIGSTKFLT"; +#endif + case SIGSTOP: return "SIGSTOP"; + case SIGSYS: return "SIGSYS"; + case SIGTRAP: return "SIGTRAP"; + case DEBUGGER_SIGNAL: return "<debuggerd signal>"; + default: return "?"; + } +} + +const char* get_sigcode(int signo, int code) { + // Try the signal-specific codes... + switch (signo) { + case SIGILL: + switch (code) { + case ILL_ILLOPC: return "ILL_ILLOPC"; + case ILL_ILLOPN: return "ILL_ILLOPN"; + case ILL_ILLADR: return "ILL_ILLADR"; + case ILL_ILLTRP: return "ILL_ILLTRP"; + case ILL_PRVOPC: return "ILL_PRVOPC"; + case ILL_PRVREG: return "ILL_PRVREG"; + case ILL_COPROC: return "ILL_COPROC"; + case ILL_BADSTK: return "ILL_BADSTK"; + } + static_assert(NSIGILL == ILL_BADSTK, "missing ILL_* si_code"); + break; + case SIGBUS: + switch (code) { + case BUS_ADRALN: return "BUS_ADRALN"; + case BUS_ADRERR: return "BUS_ADRERR"; + case BUS_OBJERR: return "BUS_OBJERR"; + case BUS_MCEERR_AR: return "BUS_MCEERR_AR"; + case BUS_MCEERR_AO: return "BUS_MCEERR_AO"; + } + static_assert(NSIGBUS == BUS_MCEERR_AO, "missing BUS_* si_code"); + break; + case SIGFPE: + switch (code) { + case FPE_INTDIV: return "FPE_INTDIV"; + case FPE_INTOVF: return "FPE_INTOVF"; + case FPE_FLTDIV: return "FPE_FLTDIV"; + case FPE_FLTOVF: return "FPE_FLTOVF"; + case FPE_FLTUND: return "FPE_FLTUND"; + case FPE_FLTRES: return "FPE_FLTRES"; + case FPE_FLTINV: return "FPE_FLTINV"; + case FPE_FLTSUB: return "FPE_FLTSUB"; + } + static_assert(NSIGFPE == FPE_FLTSUB, "missing FPE_* si_code"); + break; + case SIGSEGV: + switch (code) { + case SEGV_MAPERR: return "SEGV_MAPERR"; + case SEGV_ACCERR: return "SEGV_ACCERR"; +#if defined(SEGV_BNDERR) + case SEGV_BNDERR: return "SEGV_BNDERR"; +#endif +#if defined(SEGV_PKUERR) + case SEGV_PKUERR: return "SEGV_PKUERR"; +#endif + } +#if defined(SEGV_PKUERR) + static_assert(NSIGSEGV == SEGV_PKUERR, "missing SEGV_* si_code"); +#elif defined(SEGV_BNDERR) + static_assert(NSIGSEGV == SEGV_BNDERR, "missing SEGV_* si_code"); +#else + static_assert(NSIGSEGV == SEGV_ACCERR, "missing SEGV_* si_code"); +#endif + break; +#if defined(SYS_SECCOMP) // Our glibc is too old, and we build this for the host too. + case SIGSYS: + switch (code) { + case SYS_SECCOMP: return "SYS_SECCOMP"; + } + static_assert(NSIGSYS == SYS_SECCOMP, "missing SYS_* si_code"); + break; +#endif + case SIGTRAP: + switch (code) { + case TRAP_BRKPT: return "TRAP_BRKPT"; + case TRAP_TRACE: return "TRAP_TRACE"; + case TRAP_BRANCH: return "TRAP_BRANCH"; + case TRAP_HWBKPT: return "TRAP_HWBKPT"; + } + if ((code & 0xff) == SIGTRAP) { + switch ((code >> 8) & 0xff) { + case PTRACE_EVENT_FORK: + return "PTRACE_EVENT_FORK"; + case PTRACE_EVENT_VFORK: + return "PTRACE_EVENT_VFORK"; + case PTRACE_EVENT_CLONE: + return "PTRACE_EVENT_CLONE"; + case PTRACE_EVENT_EXEC: + return "PTRACE_EVENT_EXEC"; + case PTRACE_EVENT_VFORK_DONE: + return "PTRACE_EVENT_VFORK_DONE"; + case PTRACE_EVENT_EXIT: + return "PTRACE_EVENT_EXIT"; + case PTRACE_EVENT_SECCOMP: + return "PTRACE_EVENT_SECCOMP"; + case PTRACE_EVENT_STOP: + return "PTRACE_EVENT_STOP"; + } + } + static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code"); + break; + } + // Then the other codes... + switch (code) { + case SI_USER: return "SI_USER"; + case SI_KERNEL: return "SI_KERNEL"; + case SI_QUEUE: return "SI_QUEUE"; + case SI_TIMER: return "SI_TIMER"; + case SI_MESGQ: return "SI_MESGQ"; + case SI_ASYNCIO: return "SI_ASYNCIO"; + case SI_SIGIO: return "SI_SIGIO"; + case SI_TKILL: return "SI_TKILL"; + case SI_DETHREAD: return "SI_DETHREAD"; + } + // Then give up... + return "?"; +} |