system/corennnnn
Révision | 0528829b7378034e79820ac2d99396999bb4dd40 (tree) |
---|---|
l'heure | 2016-07-15 08:47:10 |
Auteur | Josh Gao <jmgao@goog...> |
Commiter | Josh Gao |
DO NOT MERGE: debuggerd: verify that traced threads belong to the right process.
Fix two races in debuggerd's PTRACE_ATTACH logic:
Backport of NYC change I4dfe1ea30e2c211d2389321bd66e3684dd757591
Bug: http://b/29555636
Change-Id: I320f47216b21018d3f613cfbbaaff40b3548ef36
@@ -62,8 +62,8 @@ static void dump_process_footer(log_t* log, pid_t pid) { | ||
62 | 62 | _LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid); |
63 | 63 | } |
64 | 64 | |
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) { | |
67 | 67 | char path[PATH_MAX]; |
68 | 68 | char threadnamebuf[1024]; |
69 | 69 | char* threadname = NULL; |
@@ -83,7 +83,7 @@ static void dump_thread( | ||
83 | 83 | |
84 | 84 | _LOG(log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", threadname ? threadname : "<unknown>", tid); |
85 | 85 | |
86 | - if (!attached && ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) { | |
86 | + if (!attached && !ptrace_attach_thread(pid, tid)) { | |
87 | 87 | _LOG(log, logtype::BACKTRACE, "Could not attach to thread: %s\n", strerror(errno)); |
88 | 88 | return; |
89 | 89 | } |
@@ -108,7 +108,7 @@ void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed, | ||
108 | 108 | log.amfd = amfd; |
109 | 109 | |
110 | 110 | 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); | |
112 | 112 | |
113 | 113 | char task_path[64]; |
114 | 114 | 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, | ||
126 | 126 | continue; |
127 | 127 | } |
128 | 128 | |
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); | |
130 | 130 | } |
131 | 131 | closedir(d); |
132 | 132 | } |
@@ -168,12 +168,10 @@ static int read_request(int fd, debugger_request_t* out_request) { | ||
168 | 168 | |
169 | 169 | if (msg.action == DEBUGGER_ACTION_CRASH) { |
170 | 170 | // 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); | |
177 | 175 | return -1; |
178 | 176 | } |
179 | 177 | } else if (cr.uid == 0 |
@@ -223,9 +221,32 @@ static void handle_request(int fd) { | ||
223 | 221 | // ensure that it can run as soon as we call PTRACE_CONT below. |
224 | 222 | // See details in bionic/libc/linker/debugger.c, in function |
225 | 223 | // debugger_signal_handler(). |
226 | - if (ptrace(PTRACE_ATTACH, request.tid, 0, 0)) { | |
224 | + if (!ptrace_attach_thread(request.pid, request.tid)) { | |
227 | 225 | ALOGE("ptrace attach failed: %s\n", strerror(errno)); |
228 | 226 | } 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 | + | |
229 | 250 | bool detach_failed = false; |
230 | 251 | bool attach_gdb = should_attach_gdb(&request); |
231 | 252 | if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) { |
@@ -416,7 +416,7 @@ static bool dump_sibling_thread_report( | ||
416 | 416 | } |
417 | 417 | |
418 | 418 | // 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)) { | |
420 | 420 | _LOG(log, logtype::ERROR, "ptrace attach to %d failed: %s\n", new_tid, strerror(errno)); |
421 | 421 | continue; |
422 | 422 | } |
@@ -20,6 +20,7 @@ | ||
20 | 20 | |
21 | 21 | #include <errno.h> |
22 | 22 | #include <signal.h> |
23 | +#include <stdlib.h> | |
23 | 24 | #include <string.h> |
24 | 25 | #include <unistd.h> |
25 | 26 | #include <sys/ptrace.h> |
@@ -208,3 +209,31 @@ void dump_memory(log_t* log, pid_t tid, uintptr_t addr) { | ||
208 | 209 | _LOG(log, logtype::MEMORY, " %s %s\n", code_buffer, ascii_buffer); |
209 | 210 | } |
210 | 211 | } |
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 | +} |
@@ -76,4 +76,9 @@ void wait_for_stop(pid_t tid, int* total_sleep_time_usec); | ||
76 | 76 | |
77 | 77 | void dump_memory(log_t* log, pid_t tid, uintptr_t addr); |
78 | 78 | |
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 | + | |
79 | 84 | #endif // _DEBUGGERD_UTILITY_H |