summaryrefslogtreecommitdiffstats
path: root/debuggerd/debuggerd.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'debuggerd/debuggerd.cpp')
-rw-r--r--debuggerd/debuggerd.cpp113
1 files changed, 107 insertions, 6 deletions
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 984b931..7f3fbc3 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -1,5 +1,6 @@
/*
* Copyright 2006, The Android Open Source Project
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -138,6 +139,84 @@ static int get_process_info(pid_t tid, pid_t* out_pid, uid_t* out_uid, uid_t* ou
return fields == 7 ? 0 : -1;
}
+static bool copy_file(const char* src, char* dest)
+{
+ #define BUF_SIZE 64
+ ssize_t bytes;
+ int source_fh, dest_fh;
+ int total_size = 0;
+ char buffer[BUF_SIZE];
+
+ if ((source_fh = open(src, O_RDONLY, O_NOFOLLOW)) == -1) {
+ ALOGE("Unable to open source file %s\n", src);
+ } else {
+ if((dest_fh = open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0640)) == -1) {
+ ALOGE("Unable to write destination file %s\n", dest);
+ } else {
+ while ((bytes = read(source_fh, buffer, BUF_SIZE)) > 0) {
+ if (write(dest_fh, buffer, bytes) < 0) {
+ ALOGE("Write failed for destination file %s. Copied %d bytes\n",
+ dest, total_size);
+ break;
+ }
+ total_size += bytes;
+ }
+ ALOGI("Copied %s to %s - size: %d\n", src, dest, total_size);
+ fsync(dest_fh);
+ close(dest_fh);
+ }
+ close(source_fh);
+ if (total_size > 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static void collect_etb_map(int cr_pid)
+{
+ struct stat s;
+ char src_buf[64];
+ char dest_buf[64];
+
+ snprintf(dest_buf, sizeof dest_buf, "/data/core/etb.%d", cr_pid);
+ if (!copy_file("/dev/coresight-tmc-etf", dest_buf)) {
+ ALOGE("Unable to copy ETB buffer file /dev/coresight-tmc-etf\n");
+ } else {
+ memset(src_buf, 0, sizeof(src_buf));
+ snprintf(src_buf, sizeof(src_buf), "/proc/%d/maps", cr_pid);
+ if(stat(src_buf, &s)) {
+ ALOGE("map file /proc/%d/maps does not exist for pid %d\n",
+ cr_pid, cr_pid);
+ } else {
+ snprintf(dest_buf, sizeof dest_buf, "/data/core/maps.%d", cr_pid);
+ if (!copy_file(src_buf, dest_buf)) {
+ ALOGE("Unable to copy map file /proc/%d/maps", cr_pid);
+ }
+ }
+ }
+}
+
+static void enable_etb_trace(struct ucred cr) {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("persist.debug.trace", value, "");
+ if ((strcmp(value,"1") == 0)) {
+ /* Allow ETB collection only once; Note: in future this behavior can be changed
+ * To allow this, use a property to indicate whether the ETB has been collected */
+ property_get("debug.etb.collected", value, "");
+ if(strcmp(value,"1")) {
+ ALOGI("Collecting ETB dumps (from pid=%d uid=%d)\n",
+ cr.pid, cr.uid);
+ property_set("debug.etb.collected", "1");
+ collect_etb_map(cr.pid);
+ }
+ else {
+ ALOGI("ETB already collected once, skipping (from pid=%d uid=%d)\n",
+ cr.pid, cr.uid);
+ }
+ }
+}
+
static int selinux_enabled;
/*
@@ -229,14 +308,13 @@ static int read_request(int fd, debugger_request_t* out_request) {
if (msg.action == DEBUGGER_ACTION_CRASH) {
// Ensure that the tid reported by the crashing process is valid.
- char buf[64];
- struct stat s;
- snprintf(buf, sizeof buf, "/proc/%d/task/%d", out_request->pid, out_request->tid);
- if (stat(buf, &s)) {
+ // This check needs to happen again after ptracing the requested thread to prevent a race.
+ if (!pid_contains_tid(out_request->pid, out_request->tid)) {
ALOGE("tid %d does not exist in pid %d. ignoring debug request\n",
- out_request->tid, out_request->pid);
+ out_request->tid, out_request->pid);
return -1;
}
+ enable_etb_trace(cr);
} else if (cr.uid == 0
|| (cr.uid == AID_SYSTEM && msg.action == DEBUGGER_ACTION_DUMP_BACKTRACE)) {
// Only root or system can ask us to attach to any process and dump it explicitly.
@@ -396,9 +474,32 @@ static void handle_request(int fd) {
// ensure that it can run as soon as we call PTRACE_CONT below.
// See details in bionic/libc/linker/debugger.c, in function
// debugger_signal_handler().
- if (ptrace(PTRACE_ATTACH, request.tid, 0, 0)) {
+ if (!ptrace_attach_thread(request.pid, request.tid)) {
ALOGE("ptrace attach failed: %s\n", strerror(errno));
} else {
+ // DEBUGGER_ACTION_CRASH requests can come from arbitrary processes and the tid field in
+ // the request is sent from the other side. If an attacker can cause a process to be
+ // spawned with the pid of their process, they could trick debuggerd into dumping that
+ // process by exiting after sending the request. Validate the trusted request.uid/gid
+ // to defend against this.
+ if (request.action == DEBUGGER_ACTION_CRASH) {
+ pid_t pid;
+ uid_t uid;
+ gid_t gid;
+ if (get_process_info(request.tid, &pid, &uid, &gid) != 0) {
+ ALOGE("debuggerd: failed to get process info for tid '%d'", request.tid);
+ exit(1);
+ }
+
+ if (pid != request.pid || uid != request.uid || gid != request.gid) {
+ ALOGE(
+ "debuggerd: attached task %d does not match request: "
+ "expected pid=%d,uid=%d,gid=%d, actual pid=%d,uid=%d,gid=%d",
+ request.tid, request.pid, request.uid, request.gid, pid, uid, gid);
+ exit(1);
+ }
+ }
+
bool detach_failed = false;
bool tid_unresponsive = false;
bool attach_gdb = should_attach_gdb(&request);