summaryrefslogtreecommitdiffstats
path: root/debuggerd
diff options
context:
space:
mode:
Diffstat (limited to 'debuggerd')
-rw-r--r--debuggerd/Android.mk4
-rw-r--r--debuggerd/arm/machine.c40
-rw-r--r--debuggerd/debuggerd.c83
-rw-r--r--debuggerd/symbol_table.c10
-rw-r--r--debuggerd/utility.h9
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