system/corennnnn
リビジョン | 8f7cc0e3872b0ea0e2cb3491b9f75d8186b14cc6 (tree) |
---|---|
日時 | 2016-08-16 03:29:09 |
作者 | Josh Gao <jmgao@goog...> |
コミッター | android-build-merger |
DO NOT MERGE: debuggerd: verify that traced threads belong to the right process.
am: 8d6ca194ee
Change-Id: I1459607b655da6e01bd5d59e31bdb6d79d504431
@@ -62,7 +62,7 @@ static void dump_process_footer(log_t* log, pid_t pid) { | ||
62 | 62 | _LOG(log, SCOPE_AT_FAULT, "\n----- end %d -----\n", pid); |
63 | 63 | } |
64 | 64 | |
65 | -static void dump_thread(log_t* log, pid_t tid, ptrace_context_t* context, bool attached, | |
65 | +static void dump_thread(log_t* log, pid_t pid, pid_t tid, ptrace_context_t* context, bool attached, | |
66 | 66 | bool* detach_failed, int* total_sleep_time_usec) { |
67 | 67 | char path[PATH_MAX]; |
68 | 68 | char threadnamebuf[1024]; |
@@ -84,7 +84,7 @@ static void dump_thread(log_t* log, pid_t tid, ptrace_context_t* context, bool a | ||
84 | 84 | _LOG(log, SCOPE_AT_FAULT, "\n\"%s\" sysTid=%d\n", |
85 | 85 | threadname ? threadname : "<unknown>", tid); |
86 | 86 | |
87 | - if (!attached && ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) { | |
87 | + if (!attached && !ptrace_attach_thread(pid, tid)) { | |
88 | 88 | _LOG(log, SCOPE_AT_FAULT, "Could not attach to thread: %s\n", strerror(errno)); |
89 | 89 | return; |
90 | 90 | } |
@@ -122,7 +122,7 @@ void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed, | ||
122 | 122 | |
123 | 123 | ptrace_context_t* context = load_ptrace_context(tid); |
124 | 124 | dump_process_header(&log, pid); |
125 | - dump_thread(&log, tid, context, true, detach_failed, total_sleep_time_usec); | |
125 | + dump_thread(&log, pid, tid, context, true, detach_failed, total_sleep_time_usec); | |
126 | 126 | |
127 | 127 | char task_path[64]; |
128 | 128 | snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); |
@@ -140,7 +140,7 @@ void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed, | ||
140 | 140 | continue; |
141 | 141 | } |
142 | 142 | |
143 | - dump_thread(&log, new_tid, context, false, detach_failed, total_sleep_time_usec); | |
143 | + dump_thread(&log, pid, new_tid, context, false, detach_failed, total_sleep_time_usec); | |
144 | 144 | } |
145 | 145 | closedir(d); |
146 | 146 | } |
@@ -233,13 +233,11 @@ static int read_request(int fd, debugger_request_t* out_request) { | ||
233 | 233 | |
234 | 234 | if (msg.action == DEBUGGER_ACTION_CRASH) { |
235 | 235 | /* Ensure that the tid reported by the crashing process is valid. */ |
236 | - char buf[64]; | |
237 | - struct stat s; | |
238 | - snprintf(buf, sizeof buf, "/proc/%d/task/%d", out_request->pid, out_request->tid); | |
239 | - if(stat(buf, &s)) { | |
240 | - LOG("tid %d does not exist in pid %d. ignoring debug request\n", | |
241 | - out_request->tid, out_request->pid); | |
242 | - return -1; | |
236 | + // This check needs to happen again after ptracing the requested thread to prevent a race. | |
237 | + if (!pid_contains_tid(out_request->pid, out_request->tid)) { | |
238 | + XLOG("tid %d does not exist in pid %d. ignoring debug request\n", out_request->tid, | |
239 | + out_request->pid); | |
240 | + return -1; | |
243 | 241 | } |
244 | 242 | } else if (cr.uid == 0 |
245 | 243 | || (cr.uid == AID_SYSTEM && msg.action == DEBUGGER_ACTION_DUMP_BACKTRACE)) { |
@@ -290,9 +288,32 @@ static void handle_request(int fd) { | ||
290 | 288 | * See details in bionic/libc/linker/debugger.c, in function |
291 | 289 | * debugger_signal_handler(). |
292 | 290 | */ |
293 | - if (ptrace(PTRACE_ATTACH, request.tid, 0, 0)) { | |
291 | + if (!ptrace_attach_thread(request.pid, request.tid)) { | |
294 | 292 | LOG("ptrace attach failed: %s\n", strerror(errno)); |
295 | 293 | } else { |
294 | + // DEBUGGER_ACTION_CRASH requests can come from arbitrary processes and the tid field in | |
295 | + // the request is sent from the other side. If an attacker can cause a process to be | |
296 | + // spawned with the pid of their process, they could trick debuggerd into dumping that | |
297 | + // process by exiting after sending the request. Validate the trusted request.uid/gid | |
298 | + // to defend against this. | |
299 | + if (request.action == DEBUGGER_ACTION_CRASH) { | |
300 | + pid_t pid; | |
301 | + uid_t uid; | |
302 | + gid_t gid; | |
303 | + if (get_process_info(request.tid, &pid, &uid, &gid) != 0) { | |
304 | + XLOG("debuggerd: failed to get process info for tid '%d'", request.tid); | |
305 | + exit(1); | |
306 | + } | |
307 | + | |
308 | + if (pid != request.pid || uid != request.uid || gid != request.gid) { | |
309 | + XLOG( | |
310 | + "debuggerd: attached task %d does not match request: " | |
311 | + "expected pid=%d,uid=%d,gid=%d, actual pid=%d,uid=%d,gid=%d", | |
312 | + request.tid, request.pid, request.uid, request.gid, pid, uid, gid); | |
313 | + exit(1); | |
314 | + } | |
315 | + } | |
316 | + | |
296 | 317 | bool detach_failed = false; |
297 | 318 | bool attach_gdb = should_attach_gdb(&request); |
298 | 319 | if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) { |
@@ -459,7 +459,7 @@ static bool dump_sibling_thread_report(const ptrace_context_t* context, | ||
459 | 459 | } |
460 | 460 | |
461 | 461 | /* Skip this thread if cannot ptrace it */ |
462 | - if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) { | |
462 | + if (!ptrace_attach_thread(pid, new_tid)) { | |
463 | 463 | continue; |
464 | 464 | } |
465 | 465 |
@@ -18,6 +18,7 @@ | ||
18 | 18 | #include <stddef.h> |
19 | 19 | #include <stdbool.h> |
20 | 20 | #include <stdio.h> |
21 | +#include <stdlib.h> | |
21 | 22 | #include <string.h> |
22 | 23 | #include <errno.h> |
23 | 24 | #include <unistd.h> |
@@ -128,3 +129,31 @@ void wait_for_stop(pid_t tid, int* total_sleep_time_usec) { | ||
128 | 129 | *total_sleep_time_usec += sleep_time_usec; |
129 | 130 | } |
130 | 131 | } |
132 | + | |
133 | +bool pid_contains_tid(pid_t pid, pid_t tid) { | |
134 | + char task_path[PATH_MAX]; | |
135 | + if (snprintf(task_path, PATH_MAX, "/proc/%d/task/%d", pid, tid) >= PATH_MAX) { | |
136 | + XLOG("debuggerd: task path overflow (pid = %d, tid = %d)\n", pid, tid); | |
137 | + exit(1); | |
138 | + } | |
139 | + | |
140 | + return access(task_path, F_OK) == 0; | |
141 | +} | |
142 | + | |
143 | +// Attach to a thread, and verify that it's still a member of the given process | |
144 | +bool ptrace_attach_thread(pid_t pid, pid_t tid) { | |
145 | + if (ptrace(PTRACE_ATTACH, tid, 0, 0) != 0) { | |
146 | + return false; | |
147 | + } | |
148 | + | |
149 | + // Make sure that the task we attached to is actually part of the pid we're dumping. | |
150 | + if (!pid_contains_tid(pid, tid)) { | |
151 | + if (ptrace(PTRACE_DETACH, tid, 0, 0) != 0) { | |
152 | + XLOG("debuggerd: failed to detach from thread '%d'", tid); | |
153 | + exit(1); | |
154 | + } | |
155 | + return false; | |
156 | + } | |
157 | + | |
158 | + return true; | |
159 | +} |
@@ -63,4 +63,9 @@ void _LOG(log_t* log, int scopeFlags, const char *fmt, ...) | ||
63 | 63 | int wait_for_signal(pid_t tid, int* total_sleep_time_usec); |
64 | 64 | void wait_for_stop(pid_t tid, int* total_sleep_time_usec); |
65 | 65 | |
66 | +bool pid_contains_tid(pid_t pid, pid_t tid); | |
67 | + | |
68 | +// Attach to a thread, and verify that it's still a member of the given process | |
69 | +bool ptrace_attach_thread(pid_t pid, pid_t tid); | |
70 | + | |
66 | 71 | #endif // _DEBUGGERD_UTILITY_H |