• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
Aucun tag

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

system/corennnnn


Commit MetaInfo

Révision0528829b7378034e79820ac2d99396999bb4dd40 (tree)
l'heure2016-07-15 08:47:10
AuteurJosh Gao <jmgao@goog...>
CommiterJosh Gao

Message de Log

DO NOT MERGE: debuggerd: verify that traced threads belong to the right process.

Fix two races in debuggerd's PTRACE_ATTACH logic:

  1. The target thread in a crash dump request could exit between the
    /proc/<pid>/task/<tid> check and the PTRACE_ATTACH.
  2. Sibling threads could exit between listing /proc/<pid>/task and the
    PTRACE_ATTACH.

Backport of NYC change I4dfe1ea30e2c211d2389321bd66e3684dd757591
Bug: http://b/29555636
Change-Id: I320f47216b21018d3f613cfbbaaff40b3548ef36

Change Summary

Modification

--- a/debuggerd/backtrace.cpp
+++ b/debuggerd/backtrace.cpp
@@ -62,8 +62,8 @@ static void dump_process_footer(log_t* log, pid_t pid) {
6262 _LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid);
6363 }
6464
65-static void dump_thread(
66- log_t* log, pid_t tid, bool attached, bool* detach_failed, int* total_sleep_time_usec) {
65+static void dump_thread(log_t* log, pid_t pid, pid_t tid, bool attached,
66+ bool* detach_failed, int* total_sleep_time_usec) {
6767 char path[PATH_MAX];
6868 char threadnamebuf[1024];
6969 char* threadname = NULL;
@@ -83,7 +83,7 @@ static void dump_thread(
8383
8484 _LOG(log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", threadname ? threadname : "<unknown>", tid);
8585
86- if (!attached && ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) {
86+ if (!attached && !ptrace_attach_thread(pid, tid)) {
8787 _LOG(log, logtype::BACKTRACE, "Could not attach to thread: %s\n", strerror(errno));
8888 return;
8989 }
@@ -108,7 +108,7 @@ void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed,
108108 log.amfd = amfd;
109109
110110 dump_process_header(&log, pid);
111- dump_thread(&log, tid, true, detach_failed, total_sleep_time_usec);
111+ dump_thread(&log, pid, tid, true, detach_failed, total_sleep_time_usec);
112112
113113 char task_path[64];
114114 snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
@@ -126,7 +126,7 @@ void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed,
126126 continue;
127127 }
128128
129- dump_thread(&log, new_tid, false, detach_failed, total_sleep_time_usec);
129+ dump_thread(&log, pid, new_tid, false, detach_failed, total_sleep_time_usec);
130130 }
131131 closedir(d);
132132 }
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -168,12 +168,10 @@ static int read_request(int fd, debugger_request_t* out_request) {
168168
169169 if (msg.action == DEBUGGER_ACTION_CRASH) {
170170 // Ensure that the tid reported by the crashing process is valid.
171- char buf[64];
172- struct stat s;
173- snprintf(buf, sizeof buf, "/proc/%d/task/%d", out_request->pid, out_request->tid);
174- if (stat(buf, &s)) {
175- ALOGE("tid %d does not exist in pid %d. ignoring debug request\n",
176- out_request->tid, out_request->pid);
171+ // This check needs to happen again after ptracing the requested thread to prevent a race.
172+ if (!pid_contains_tid(out_request->pid, out_request->tid)) {
173+ ALOGE("tid %d does not exist in pid %d. ignoring debug request\n", out_request->tid,
174+ out_request->pid);
177175 return -1;
178176 }
179177 } else if (cr.uid == 0
@@ -223,9 +221,32 @@ static void handle_request(int fd) {
223221 // ensure that it can run as soon as we call PTRACE_CONT below.
224222 // See details in bionic/libc/linker/debugger.c, in function
225223 // debugger_signal_handler().
226- if (ptrace(PTRACE_ATTACH, request.tid, 0, 0)) {
224+ if (!ptrace_attach_thread(request.pid, request.tid)) {
227225 ALOGE("ptrace attach failed: %s\n", strerror(errno));
228226 } else {
227+ // DEBUGGER_ACTION_CRASH requests can come from arbitrary processes and the tid field in
228+ // the request is sent from the other side. If an attacker can cause a process to be
229+ // spawned with the pid of their process, they could trick debuggerd into dumping that
230+ // process by exiting after sending the request. Validate the trusted request.uid/gid
231+ // to defend against this.
232+ if (request.action == DEBUGGER_ACTION_CRASH) {
233+ pid_t pid;
234+ uid_t uid;
235+ gid_t gid;
236+ if (get_process_info(request.tid, &pid, &uid, &gid) != 0) {
237+ ALOGE("debuggerd: failed to get process info for tid '%d'", request.tid);
238+ exit(1);
239+ }
240+
241+ if (pid != request.pid || uid != request.uid || gid != request.gid) {
242+ ALOGE(
243+ "debuggerd: attached task %d does not match request: "
244+ "expected pid=%d,uid=%d,gid=%d, actual pid=%d,uid=%d,gid=%d",
245+ request.tid, request.pid, request.uid, request.gid, pid, uid, gid);
246+ exit(1);
247+ }
248+ }
249+
229250 bool detach_failed = false;
230251 bool attach_gdb = should_attach_gdb(&request);
231252 if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) {
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/tombstone.cpp
@@ -416,7 +416,7 @@ static bool dump_sibling_thread_report(
416416 }
417417
418418 // Skip this thread if cannot ptrace it
419- if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) {
419+ if (!ptrace_attach_thread(pid, new_tid)) {
420420 _LOG(log, logtype::ERROR, "ptrace attach to %d failed: %s\n", new_tid, strerror(errno));
421421 continue;
422422 }
--- a/debuggerd/utility.cpp
+++ b/debuggerd/utility.cpp
@@ -20,6 +20,7 @@
2020
2121 #include <errno.h>
2222 #include <signal.h>
23+#include <stdlib.h>
2324 #include <string.h>
2425 #include <unistd.h>
2526 #include <sys/ptrace.h>
@@ -208,3 +209,31 @@ void dump_memory(log_t* log, pid_t tid, uintptr_t addr) {
208209 _LOG(log, logtype::MEMORY, " %s %s\n", code_buffer, ascii_buffer);
209210 }
210211 }
212+
213+bool pid_contains_tid(pid_t pid, pid_t tid) {
214+ char task_path[PATH_MAX];
215+ if (snprintf(task_path, PATH_MAX, "/proc/%d/task/%d", pid, tid) >= PATH_MAX) {
216+ ALOGE("debuggerd: task path overflow (pid = %d, tid = %d)\n", pid, tid);
217+ exit(1);
218+ }
219+
220+ return access(task_path, F_OK) == 0;
221+}
222+
223+// Attach to a thread, and verify that it's still a member of the given process
224+bool ptrace_attach_thread(pid_t pid, pid_t tid) {
225+ if (ptrace(PTRACE_ATTACH, tid, 0, 0) != 0) {
226+ return false;
227+ }
228+
229+ // Make sure that the task we attached to is actually part of the pid we're dumping.
230+ if (!pid_contains_tid(pid, tid)) {
231+ if (ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
232+ ALOGE("debuggerd: failed to detach from thread '%d'", tid);
233+ exit(1);
234+ }
235+ return false;
236+ }
237+
238+ return true;
239+}
--- a/debuggerd/utility.h
+++ b/debuggerd/utility.h
@@ -76,4 +76,9 @@ void wait_for_stop(pid_t tid, int* total_sleep_time_usec);
7676
7777 void dump_memory(log_t* log, pid_t tid, uintptr_t addr);
7878
79+bool pid_contains_tid(pid_t pid, pid_t tid);
80+
81+// Attach to a thread, and verify that it's still a member of the given process
82+bool ptrace_attach_thread(pid_t pid, pid_t tid);
83+
7984 #endif // _DEBUGGERD_UTILITY_H