diff options
Diffstat (limited to 'debuggerd/debuggerd.c')
-rw-r--r-- | debuggerd/debuggerd.c | 243 |
1 files changed, 163 insertions, 80 deletions
diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c index 7a3e781..91d9dda 100644 --- a/debuggerd/debuggerd.c +++ b/debuggerd/debuggerd.c @@ -31,13 +31,13 @@ #include <cutils/sockets.h> #include <cutils/logd.h> +#include <cutils/logger.h> #include <cutils/properties.h> #include <linux/input.h> #include <private/android_filesystem_config.h> -#include <byteswap.h> #include "debuggerd.h" #include "utility.h" @@ -71,14 +71,17 @@ mapinfo *parse_maps_line(char *line) mapinfo *mi; int len = strlen(line); - if(len < 1) return 0; + if (len < 1) return 0; /* not expected */ line[--len] = 0; - if(len < 50) return 0; - if(line[20] != 'x') return 0; + if (len < 50) { + mi = malloc(sizeof(mapinfo) + 1); + } else { + mi = malloc(sizeof(mapinfo) + (len - 47)); + } + if (mi == 0) return 0; - mi = malloc(sizeof(mapinfo) + (len - 47)); - if(mi == 0) return 0; + mi->isExecutable = (line[20] == 'x'); mi->start = strtoul(line, 0, 16); mi->end = strtoul(line + 9, 0, 16); @@ -88,7 +91,11 @@ mapinfo *parse_maps_line(char *line) mi->exidx_start = mi->exidx_end = 0; mi->symbols = 0; mi->next = 0; - strcpy(mi->name, line + 49); + if (len < 50) { + mi->name[0] = '\0'; + } else { + strcpy(mi->name, line + 49); + } return mi; } @@ -166,11 +173,14 @@ void dump_fault_addr(int tfd, int pid, int sig) memset(&si, 0, sizeof(si)); if(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)){ _LOG(tfd, false, "cannot get siginfo: %s\n", strerror(errno)); - } else { + } else if (signal_has_address(sig)) { _LOG(tfd, false, "signal %d (%s), code %d (%s), fault addr %08x\n", sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code), si.si_addr); + } else { + _LOG(tfd, false, "signal %d (%s), code %d (%s), fault addr --------\n", + sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code)); } } @@ -196,77 +206,13 @@ void dump_crash_banner(int tfd, unsigned pid, unsigned tid, int sig) if(sig) dump_fault_addr(tfd, tid, sig); } -/* After randomization (ASLR), stack contents that point to randomized - * code become uninterpretable (e.g. can't be resolved to line numbers). - * Here, we bundle enough information so that stack analysis on the - * server side can still be performed. This means we are leaking some - * information about the device (its randomization base). We have to make - * sure an attacker has no way of intercepting the tombstone. - */ - -typedef struct { - int32_t mmap_addr; - char tag[4]; /* 'P', 'R', 'E', ' ' */ -} prelink_info_t __attribute__((packed)); - -static inline void set_prelink(long *prelink_addr, - prelink_info_t *info) -{ - // We will assume the binary is little-endian, and test the - // host endianness here. - unsigned long test_endianness = 0xFF; - - if (sizeof(prelink_info_t) == 8 && prelink_addr) { - if (*(unsigned char *)&test_endianness) - *prelink_addr = info->mmap_addr; - else - *prelink_addr = bswap_32(info->mmap_addr); - } -} - -static int check_prelinked(const char *fname, - long *prelink_addr) -{ - *prelink_addr = 0; - if (sizeof(prelink_info_t) != 8) return 0; - - int fd = open(fname, O_RDONLY); - if (fd < 0) return 0; - off_t end = lseek(fd, 0, SEEK_END); - int nr = sizeof(prelink_info_t); - - off_t sz = lseek(fd, -nr, SEEK_CUR); - if ((long)(end - sz) != (long)nr) return 0; - if (sz == (off_t)-1) return 0; - - prelink_info_t info; - int num_read = read(fd, &info, nr); - if (num_read < 0) return 0; - if (num_read != sizeof(info)) return 0; - - int prelinked = 0; - if (!strncmp(info.tag, "PRE ", 4)) { - set_prelink(prelink_addr, &info); - prelinked = 1; - } - if (close(fd) < 0) return 0; - return prelinked; -} - -void dump_randomization_base(int tfd, bool at_fault) { - bool only_in_tombstone = !at_fault; - long prelink_addr; - check_prelinked("/system/lib/libc.so", &prelink_addr); - _LOG(tfd, only_in_tombstone, - "\nlibc base address: %08x\n", prelink_addr); -} - -/* End of ASLR-related logic. */ - static void parse_elf_info(mapinfo *milist, pid_t pid) { mapinfo *mi; for (mi = milist; mi != NULL; mi = mi->next) { + if (!mi->isExecutable) + continue; + Elf32_Ehdr ehdr; memset(&ehdr, 0, sizeof(Elf32_Ehdr)); @@ -353,7 +299,6 @@ void dump_crash_report(int tfd, unsigned pid, unsigned tid, bool at_fault) dump_pc_and_lr(tfd, tid, milist, stack_depth, at_fault); } - dump_randomization_base(tfd, at_fault); dump_stack_and_code(tfd, tid, milist, stack_depth, sp_list, at_fault); #elif __i386__ /* If stack unwinder fails, use the default solution to dump the stack @@ -469,6 +414,101 @@ static bool dump_sibling_thread_report(int tfd, unsigned pid, unsigned tid) return need_cleanup != 0; } +/* + * Reads the contents of the specified log device, filters out the entries + * that don't match the specified pid, and writes them to the tombstone file. + */ +static void dump_log_file(int tfd, unsigned pid, const char* filename) +{ + int logfd = open(filename, O_RDONLY | O_NONBLOCK); + if (logfd < 0) { + XLOG("Unable to open %s: %s\n", filename, strerror(errno)); + return; + } + _LOG(tfd, true, "--------- log %s\n", filename); + + union { + unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; + struct logger_entry entry; + } log_entry; + + while (true) { + ssize_t actual = read(logfd, log_entry.buf, LOGGER_ENTRY_MAX_LEN); + if (actual < 0) { + if (errno == EINTR) { + /* interrupted by signal, retry */ + continue; + } else if (errno == EAGAIN) { + /* non-blocking EOF; we're done */ + break; + } else { + _LOG(tfd, true, "Error while reading log: %s\n", + strerror(errno)); + break; + } + } else if (actual == 0) { + _LOG(tfd, true, "Got zero bytes while reading log: %s\n", + strerror(errno)); + break; + } + + /* + * NOTE: if you XLOG something here, this will spin forever, + * because you will be writing as fast as you're reading. Any + * high-frequency debug diagnostics should just be written to + * the tombstone file. + */ + + struct logger_entry* entry = &log_entry.entry; + + if (entry->pid != (int32_t) pid) { + /* wrong pid, ignore */ + continue; + } + + /* + * Msg format is: <priority:1><tag:N>\0<message:N>\0 + * + * We want to display it in the same format as "logcat -v threadtime" + * (although in this case the pid is redundant). + * + * TODO: scan for line breaks ('\n') and display each text line + * on a separate line, prefixed with the header, like logcat does. + */ + static const char* kPrioChars = "!.VDIWEFS"; + unsigned char prio = entry->msg[0]; + const char* tag = entry->msg + 1; + const char* msg = tag + strlen(tag) + 1; + + log_entry.entry.msg[entry->len] = '\0'; + + char timeBuf[32]; + time_t sec = (time_t) entry->sec; + struct tm tmBuf; + struct tm* ptm; + ptm = localtime_r(&sec, &tmBuf); + strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); + + _LOG(tfd, true, "%s.%03ld %5d %5d %c %-8s: %s\n", + timeBuf, entry->nsec / 1000000, + entry->pid, entry->tid, + (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?'), + tag, msg); + } + + close(logfd); +} + +/* + * Dumps the logs generated by the specified pid to the tombstone, from both + * "system" and "main" log devices. Ideally we'd interleave the output. + */ +static void dump_logs(int tfd, unsigned pid) +{ + dump_log_file(tfd, pid, "/dev/log/system"); + dump_log_file(tfd, pid, "/dev/log/main"); +} + /* Return true if some thread is not detached cleanly */ static bool engrave_tombstone(unsigned pid, unsigned tid, int debug_uid, int signal) @@ -493,6 +533,13 @@ static bool engrave_tombstone(unsigned pid, unsigned tid, int debug_uid, need_cleanup = dump_sibling_thread_report(fd, pid, tid); } + /* don't copy log to tombstone unless this is a dev device */ + char value[PROPERTY_VALUE_MAX]; + property_get("ro.debuggable", value, "0"); + if (value[0] == '1') { + dump_logs(fd, pid); + } + close(fd); return need_cleanup; } @@ -662,19 +709,39 @@ static void handle_crashing_process(int fd) * debugger_signal_handler(). */ tid_attach_status = ptrace(PTRACE_ATTACH, tid, 0, 0); + int ptrace_error = errno; - TEMP_FAILURE_RETRY(write(fd, &tid, 1)); + if (TEMP_FAILURE_RETRY(write(fd, &tid, 1)) != 1) { + XLOG("failed responding to client: %s\n", + strerror(errno)); + goto done; + } if(tid_attach_status < 0) { - LOG("ptrace attach failed: %s\n", strerror(errno)); + LOG("ptrace attach failed: %s\n", strerror(ptrace_error)); goto done; } close(fd); fd = -1; + const int sleep_time_usec = 200000; /* 0.2 seconds */ + const int max_total_sleep_usec = 3000000; /* 3 seconds */ + int loop_limit = max_total_sleep_usec / sleep_time_usec; for(;;) { - n = waitpid(tid, &status, __WALL); + if (loop_limit-- == 0) { + LOG("timed out waiting for pid=%d tid=%d uid=%d to die\n", + cr.pid, tid, cr.uid); + goto done; + } + n = waitpid(tid, &status, __WALL | WNOHANG); + + if (n == 0) { + /* not ready yet */ + XLOG("not ready yet\n"); + usleep(sleep_time_usec); + continue; + } if(n < 0) { if(errno == EAGAIN) continue; @@ -764,6 +831,18 @@ int main() struct sigaction act; int logsocket = -1; + /* + * debuggerd crashes can't be reported to debuggerd. Reset all of the + * crash handlers. + */ + signal(SIGILL, SIG_DFL); + signal(SIGABRT, SIG_DFL); + signal(SIGBUS, SIG_DFL); + signal(SIGFPE, SIG_DFL); + signal(SIGSEGV, SIG_DFL); + signal(SIGSTKFLT, SIG_DFL); + signal(SIGPIPE, SIG_DFL); + logsocket = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM); if(logsocket < 0) { @@ -791,8 +870,12 @@ int main() int fd; alen = sizeof(addr); + XLOG("waiting for connection\n"); fd = accept(s, &addr, &alen); - if(fd < 0) continue; + if(fd < 0) { + XLOG("accept failed: %s\n", strerror(errno)); + continue; + } fcntl(fd, F_SETFD, FD_CLOEXEC); |