diff options
Diffstat (limited to 'debuggerd')
-rw-r--r-- | debuggerd/Android.mk | 4 | ||||
-rw-r--r-- | debuggerd/arm/machine.c | 40 | ||||
-rw-r--r-- | debuggerd/debuggerd.c | 83 | ||||
-rw-r--r-- | debuggerd/symbol_table.c | 10 | ||||
-rw-r--r-- | debuggerd/utility.h | 9 |
5 files changed, 128 insertions, 18 deletions
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk index 752c953..6cfe79b 100644 --- a/debuggerd/Android.mk +++ b/debuggerd/Android.mk @@ -29,7 +29,7 @@ LOCAL_SRC_FILES := crasher.c LOCAL_SRC_FILES += $(TARGET_ARCH)/crashglue.S LOCAL_MODULE := crasher LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) -LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_TAGS := optional #LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_SHARED_LIBRARIES := libcutils libc include $(BUILD_EXECUTABLE) @@ -45,7 +45,7 @@ endif # ARCH_ARM_HAVE_VFP_D32 LOCAL_SRC_FILES := vfp-crasher.c vfp.S LOCAL_MODULE := vfp-crasher LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) -LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_TAGS := optional LOCAL_SHARED_LIBRARIES := libcutils libc include $(BUILD_EXECUTABLE) endif # ARCH_ARM_HAVE_VFP == true diff --git a/debuggerd/arm/machine.c b/debuggerd/arm/machine.c index ccd0baf..88bf054 100644 --- a/debuggerd/arm/machine.c +++ b/debuggerd/arm/machine.c @@ -32,6 +32,7 @@ #include <cutils/properties.h> #include <linux/input.h> +#include <linux/user.h> #include "utility.h" @@ -53,7 +54,7 @@ void dump_stack_and_code(int tfd, int pid, mapinfo *map, int unwind_depth, unsigned int sp_list[], bool at_fault) { - unsigned int sp, pc, p, end, data; + unsigned int sp, pc, lr, p, end, data; struct pt_regs r; int sp_depth; bool only_in_tombstone = !at_fault; @@ -62,19 +63,25 @@ void dump_stack_and_code(int tfd, int pid, mapinfo *map, if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return; sp = r.ARM_sp; pc = r.ARM_pc; + lr = r.ARM_lr; _LOG(tfd, only_in_tombstone, "\ncode around pc:\n"); - end = p = pc & ~3; + p = pc & ~3; p -= 32; - end += 32; + if (p > pc) + p = 0; + end = p + 80; + /* 'end - p' has to be multiples of 16 */ + while (end < p) + end -= 16; /* Dump the code around PC as: * addr contents * 00008d34 fffffcd0 4c0eb530 b0934a0e 1c05447c * 00008d44 f7ff18a0 490ced94 68035860 d0012b00 */ - while (p <= end) { + while (p < end) { int i; sprintf(code_buffer, "%08x ", p); @@ -86,19 +93,24 @@ void dump_stack_and_code(int tfd, int pid, mapinfo *map, _LOG(tfd, only_in_tombstone, "%s\n", code_buffer); } - if ((unsigned) r.ARM_lr != pc) { + if (lr != pc) { _LOG(tfd, only_in_tombstone, "\ncode around lr:\n"); - end = p = r.ARM_lr & ~3; + p = lr & ~3; p -= 32; - end += 32; + if (p > lr) + p = 0; + end = p + 80; + /* 'end - p' has to be multiples of 16 */ + while (end < p) + end -= 16; /* Dump the code around LR as: * addr contents * 00008d34 fffffcd0 4c0eb530 b0934a0e 1c05447c * 00008d44 f7ff18a0 490ced94 68035860 d0012b00 */ - while (p <= end) { + while (p < end) { int i; sprintf(code_buffer, "%08x ", p); @@ -112,6 +124,8 @@ void dump_stack_and_code(int tfd, int pid, mapinfo *map, } p = sp - 64; + if (p > sp) + p = 0; p &= ~3; if (unwind_depth != 0) { if (unwind_depth < STACK_CONTENT_DEPTH) { @@ -122,8 +136,10 @@ void dump_stack_and_code(int tfd, int pid, mapinfo *map, } } else { - end = sp | 0x000000ff; - end += 0xff; + end = p + 256; + /* 'end - p' has to be multiples of 4 */ + if (end < p) + end = ~7; } _LOG(tfd, only_in_tombstone, "\nstack:\n"); @@ -161,6 +177,10 @@ void dump_stack_and_code(int tfd, int pid, mapinfo *map, /* print another 64-byte of stack data after the last frame */ end = p+64; + /* 'end - p' has to be multiples of 4 */ + if (end < p) + end = ~7; + while (p <= end) { data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); _LOG(tfd, (sp_depth > 2) || only_in_tombstone, diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c index ecfe01f..7a3e781 100644 --- a/debuggerd/debuggerd.c +++ b/debuggerd/debuggerd.c @@ -37,6 +37,7 @@ #include <private/android_filesystem_config.h> +#include <byteswap.h> #include "debuggerd.h" #include "utility.h" @@ -195,6 +196,73 @@ 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; @@ -285,6 +353,7 @@ 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 @@ -573,7 +642,7 @@ static void handle_crashing_process(int fd) goto done; } - sprintf(buf,"/proc/%d/task/%d", cr.pid, tid); + snprintf(buf, sizeof buf, "/proc/%d/task/%d", cr.pid, tid); if(stat(buf, &s)) { LOG("tid %d does not exist in pid %d. ignoring debug request\n", tid, cr.pid); @@ -583,7 +652,19 @@ static void handle_crashing_process(int fd) XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", cr.pid, cr.uid, cr.gid, tid); + /* Note that at this point, the target thread's signal handler + * is blocked in a read() call. This gives us the time to PTRACE_ATTACH + * to it before it has a chance to really fault. + * + * After the attach, the thread is stopped, and we write to the file + * descriptor to ensure that it will run as soon as we call PTRACE_CONT + * below. See details in bionic/libc/linker/debugger.c, in function + * debugger_signal_handler(). + */ tid_attach_status = ptrace(PTRACE_ATTACH, tid, 0, 0); + + TEMP_FAILURE_RETRY(write(fd, &tid, 1)); + if(tid_attach_status < 0) { LOG("ptrace attach failed: %s\n", strerror(errno)); goto done; diff --git a/debuggerd/symbol_table.c b/debuggerd/symbol_table.c index fd008fe..23572a3 100644 --- a/debuggerd/symbol_table.c +++ b/debuggerd/symbol_table.c @@ -50,7 +50,7 @@ struct symbol_table *symbol_table_create(const char *filename) int length; char *base; - XLOG("Creating symbol table for %s\n", filename); + XLOG2("Creating symbol table for %s\n", filename); int fd = open(filename, O_RDONLY); if(fd < 0) { @@ -126,7 +126,7 @@ struct symbol_table *symbol_table_create(const char *filename) dynsymbol_count++; } } - XLOG("Dynamic Symbol count: %d\n", dynsymbol_count); + XLOG2("Dynamic Symbol count: %d\n", dynsymbol_count); } if (sym_idx != -1) { @@ -139,7 +139,7 @@ struct symbol_table *symbol_table_create(const char *filename) symbol_count++; } } - XLOG("Symbol count: %d\n", symbol_count); + XLOG2("Symbol count: %d\n", symbol_count); } // Now, create an entry in our symbol table structure for each symbol... @@ -160,7 +160,7 @@ struct symbol_table *symbol_table_create(const char *filename) table->symbols[j].name = strdup(dynstr + dynsyms[i].st_name); table->symbols[j].addr = dynsyms[i].st_value; table->symbols[j].size = dynsyms[i].st_size; - XLOG("name: %s, addr: %x, size: %x\n", + XLOG2("name: %s, addr: %x, size: %x\n", table->symbols[j].name, table->symbols[j].addr, table->symbols[j].size); j++; } @@ -176,7 +176,7 @@ struct symbol_table *symbol_table_create(const char *filename) table->symbols[j].name = strdup(str + syms[i].st_name); table->symbols[j].addr = syms[i].st_value; table->symbols[j].size = syms[i].st_size; - XLOG("name: %s, addr: %x, size: %x\n", + XLOG2("name: %s, addr: %x, size: %x\n", table->symbols[j].name, table->symbols[j].addr, table->symbols[j].size); j++; } diff --git a/debuggerd/utility.h b/debuggerd/utility.h index 0682b85..45e2067 100644 --- a/debuggerd/utility.h +++ b/debuggerd/utility.h @@ -57,10 +57,19 @@ const char *map_to_name(mapinfo *mi, unsigned pc, const char* def); extern void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...); #define LOG(fmt...) _LOG(-1, 0, fmt) + +/* Set to 1 for normal debug traces */ #if 0 #define XLOG(fmt...) _LOG(-1, 0, fmt) #else #define XLOG(fmt...) do {} while(0) #endif +/* Set to 1 for chatty debug traces. Includes all resolved dynamic symbols */ +#if 0 +#define XLOG2(fmt...) _LOG(-1, 0, fmt) +#else +#define XLOG2(fmt...) do {} while(0) +#endif + #endif |