system/core
リビジョン | cf94d3ac8f65794b5c913a940c652a88e3696e72 (tree) |
---|---|
日時 | 2016-09-08 04:31:21 |
作者 | Josh Gao <jmgao@goog...> |
コミッター | Dennis Cagle |
debuggerd: verify that traced threads belong to the right process.
Fix two races in debuggerd's PTRACE_ATTACH logic:
Bug: http://b/29555636
Change-Id: I4dfe1ea30e2c211d2389321bd66e3684dd757591
(cherry picked from commit d7603583f90c2bc6074a4ee2886bd28082d7c65b)
@@ -183,6 +183,16 @@ out: | ||
183 | 183 | return allowed; |
184 | 184 | } |
185 | 185 | |
186 | +static bool pid_contains_tid(pid_t pid, pid_t tid) { | |
187 | + char task_path[PATH_MAX]; | |
188 | + if (snprintf(task_path, PATH_MAX, "/proc/%d/task/%d", pid, tid) >= PATH_MAX) { | |
189 | + ALOGE("debuggerd: task path overflow (pid = %d, tid = %d)\n", pid, tid); | |
190 | + exit(1); | |
191 | + } | |
192 | + | |
193 | + return access(task_path, F_OK) == 0; | |
194 | +} | |
195 | + | |
186 | 196 | static int read_request(int fd, debugger_request_t* out_request) { |
187 | 197 | ucred cr; |
188 | 198 | socklen_t len = sizeof(cr); |
@@ -227,16 +237,13 @@ static int read_request(int fd, debugger_request_t* out_request) { | ||
227 | 237 | |
228 | 238 | if (msg.action == DEBUGGER_ACTION_CRASH) { |
229 | 239 | // Ensure that the tid reported by the crashing process is valid. |
230 | - char buf[64]; | |
231 | - struct stat s; | |
232 | - snprintf(buf, sizeof buf, "/proc/%d/task/%d", out_request->pid, out_request->tid); | |
233 | - if (stat(buf, &s)) { | |
234 | - ALOGE("tid %d does not exist in pid %d. ignoring debug request\n", | |
235 | - out_request->tid, out_request->pid); | |
240 | + // This check needs to happen again after ptracing the requested thread to prevent a race. | |
241 | + if (!pid_contains_tid(out_request->pid, out_request->tid)) { | |
242 | + ALOGE("tid %d does not exist in pid %d. ignoring debug request\n", out_request->tid, | |
243 | + out_request->pid); | |
236 | 244 | return -1; |
237 | 245 | } |
238 | - } else if (cr.uid == 0 | |
239 | - || (cr.uid == AID_SYSTEM && msg.action == DEBUGGER_ACTION_DUMP_BACKTRACE)) { | |
246 | + } else if (cr.uid == 0 || (cr.uid == AID_SYSTEM && msg.action == DEBUGGER_ACTION_DUMP_BACKTRACE)) { | |
240 | 247 | // Only root or system can ask us to attach to any process and dump it explicitly. |
241 | 248 | // However, system is only allowed to collect backtraces but cannot dump tombstones. |
242 | 249 | status = get_process_info(out_request->tid, &out_request->pid, |
@@ -413,10 +420,31 @@ static void redirect_to_32(int fd, debugger_request_t* request) { | ||
413 | 420 | } |
414 | 421 | #endif |
415 | 422 | |
423 | +// Attach to a thread, and verify that it's still a member of the given process | |
424 | +static bool ptrace_attach_thread(pid_t pid, pid_t tid) { | |
425 | + if (ptrace(PTRACE_ATTACH, tid, 0, 0) != 0) { | |
426 | + return false; | |
427 | + } | |
428 | + | |
429 | + // Make sure that the task we attached to is actually part of the pid we're dumping. | |
430 | + if (!pid_contains_tid(pid, tid)) { | |
431 | + if (ptrace(PTRACE_DETACH, tid, 0, 0) != 0) { | |
432 | + ALOGE("debuggerd: failed to detach from thread '%d'", tid); | |
433 | + exit(1); | |
434 | + } | |
435 | + return false; | |
436 | + } | |
437 | + | |
438 | + return true; | |
439 | +} | |
440 | + | |
416 | 441 | static void ptrace_siblings(pid_t pid, pid_t main_tid, std::set<pid_t>& tids) { |
417 | - char task_path[64]; | |
442 | + char task_path[PATH_MAX]; | |
418 | 443 | |
419 | - snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); | |
444 | + if (snprintf(task_path, PATH_MAX, "/proc/%d/task", pid) >= PATH_MAX) { | |
445 | + ALOGE("debuggerd: task path overflow (pid = %d)\n", pid); | |
446 | + abort(); | |
447 | + } | |
420 | 448 | |
421 | 449 | std::unique_ptr<DIR, int (*)(DIR*)> d(opendir(task_path), closedir); |
422 | 450 |
@@ -443,7 +471,7 @@ static void ptrace_siblings(pid_t pid, pid_t main_tid, std::set<pid_t>& tids) { | ||
443 | 471 | continue; |
444 | 472 | } |
445 | 473 | |
446 | - if (ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) { | |
474 | + if (!ptrace_attach_thread(pid, tid)) { | |
447 | 475 | ALOGE("debuggerd: ptrace attach to %d failed: %s", tid, strerror(errno)); |
448 | 476 | continue; |
449 | 477 | } |
@@ -568,11 +596,33 @@ static void worker_process(int fd, debugger_request_t& request) { | ||
568 | 596 | // debugger_signal_handler(). |
569 | 597 | |
570 | 598 | // Attach to the target process. |
571 | - if (ptrace(PTRACE_ATTACH, request.tid, 0, 0) != 0) { | |
599 | + if (!ptrace_attach_thread(request.pid, request.tid)) { | |
572 | 600 | ALOGE("debuggerd: ptrace attach failed: %s", strerror(errno)); |
573 | 601 | exit(1); |
574 | 602 | } |
575 | 603 | |
604 | + // DEBUGGER_ACTION_CRASH requests can come from arbitrary processes and the tid field in the | |
605 | + // request is sent from the other side. If an attacker can cause a process to be spawned with the | |
606 | + // pid of their process, they could trick debuggerd into dumping that process by exiting after | |
607 | + // sending the request. Validate the trusted request.uid/gid to defend against this. | |
608 | + if (request.action == DEBUGGER_ACTION_CRASH) { | |
609 | + pid_t pid; | |
610 | + uid_t uid; | |
611 | + gid_t gid; | |
612 | + if (get_process_info(request.tid, &pid, &uid, &gid) != 0) { | |
613 | + ALOGE("debuggerd: failed to get process info for tid '%d'", request.tid); | |
614 | + exit(1); | |
615 | + } | |
616 | + | |
617 | + if (pid != request.pid || uid != request.uid || gid != request.gid) { | |
618 | + ALOGE( | |
619 | + "debuggerd: attached task %d does not match request: " | |
620 | + "expected pid=%d,uid=%d,gid=%d, actual pid=%d,uid=%d,gid=%d", | |
621 | + request.tid, request.pid, request.uid, request.gid, pid, uid, gid); | |
622 | + exit(1); | |
623 | + } | |
624 | + } | |
625 | + | |
576 | 626 | // Don't attach to the sibling threads if we want to attach gdb. |
577 | 627 | // Supposedly, it makes the process less reliable. |
578 | 628 | bool attach_gdb = should_attach_gdb(request); |