summaryrefslogtreecommitdiffstats
path: root/debuggerd/debuggerd.c
diff options
context:
space:
mode:
Diffstat (limited to 'debuggerd/debuggerd.c')
-rw-r--r--debuggerd/debuggerd.c243
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);