From 2f11a25a48897efe35d065472f921759d5941eba Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Mon, 13 Feb 2017 14:46:19 -0800 Subject: [PATCH] debuggerd_handler: set PR_SET_DUMPABLE before running crash_dump. Set and restore PR_SET_DUMPABLE when performing a dump, so that processes that have it implicitly cleared (e.g. services that acquire filesystem capabilities) still get crash dumps. Bug: http://b/35174939 Test: debuggerd -b `pidof surfaceflinger` Change-Id: Ife933c10086e546726dec12a7efa3f9cedfeea60 --- debuggerd/crash_dump.cpp | 80 +++++++++++++------------ debuggerd/handler/debuggerd_handler.cpp | 19 +++++- 2 files changed, 59 insertions(+), 40 deletions(-) diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp index 288935611..0ca90c3df 100644 --- a/debuggerd/crash_dump.cpp +++ b/debuggerd/crash_dump.cpp @@ -238,11 +238,12 @@ int main(int argc, char** argv) { action.sa_handler = signal_handler; debuggerd_register_handlers(&action); - if (argc != 2) { + if (argc != 3) { return 1; } pid_t main_tid; + pid_t pseudothread_tid; if (target == 1) { LOG(FATAL) << "target died before we could attach"; @@ -252,6 +253,10 @@ int main(int argc, char** argv) { LOG(FATAL) << "invalid main tid: " << argv[1]; } + if (!android::base::ParseInt(argv[2], &pseudothread_tid, 1, std::numeric_limits::max())) { + LOG(FATAL) << "invalid pseudothread tid: " << argv[1]; + } + android::procinfo::ProcessInfo target_info; if (!android::procinfo::GetProcessInfo(main_tid, &target_info)) { LOG(FATAL) << "failed to fetch process info for target " << main_tid; @@ -284,16 +289,53 @@ int main(int argc, char** argv) { check_process(target_proc_fd, target); std::string attach_error; + + // Seize the main thread. if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) { LOG(FATAL) << attach_error; } + // Seize the siblings. + std::set attached_siblings; + { + std::set siblings; + if (!android::procinfo::GetProcessTids(target, &siblings)) { + PLOG(FATAL) << "failed to get process siblings"; + } + + // but not the already attached main thread. + siblings.erase(main_tid); + // or the handler pseudothread. + siblings.erase(pseudothread_tid); + + for (pid_t sibling_tid : siblings) { + if (!ptrace_seize_thread(target_proc_fd, sibling_tid, &attach_error)) { + LOG(WARNING) << attach_error; + } else { + attached_siblings.insert(sibling_tid); + } + } + } + + // Collect the backtrace map and open files, while the process still has PR_GET_DUMPABLE=1 + std::unique_ptr backtrace_map(BacktraceMap::Create(main_tid)); + if (!backtrace_map) { + LOG(FATAL) << "failed to create backtrace map"; + } + + // Collect the list of open files. + OpenFilesList open_files; + populate_open_files_list(target, &open_files); + + // Drop our capabilities now that we've attached to the threads we care about. + drop_capabilities(); check_process(target_proc_fd, target); LOG(INFO) << "obtaining output fd from tombstoned"; tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd); // Write a '\1' to stdout to tell the crashing process to resume. + // It also restores the value of PR_SET_DUMPABLE at this point. if (TEMP_FAILURE_RETRY(write(STDOUT_FILENO, "\1", 1)) == -1) { PLOG(ERROR) << "failed to communicate to target process"; } @@ -339,45 +381,9 @@ int main(int argc, char** argv) { abort_address = reinterpret_cast(siginfo.si_value.sival_ptr); } - // Now that we have the signal that kicked things off, attach all of the - // sibling threads, and then proceed. - std::set attached_siblings; - { - std::set siblings; - if (!android::procinfo::GetProcessTids(target, &siblings)) { - PLOG(FATAL) << "failed to get process siblings"; - } - siblings.erase(main_tid); - - for (pid_t sibling_tid : siblings) { - if (!ptrace_seize_thread(target_proc_fd, sibling_tid, &attach_error)) { - LOG(WARNING) << attach_error; - } else { - attached_siblings.insert(sibling_tid); - } - } - } - - std::unique_ptr backtrace_map(BacktraceMap::Create(main_tid)); - if (!backtrace_map) { - LOG(FATAL) << "failed to create backtrace map"; - } - - // Collect the list of open files. - OpenFilesList open_files; - if (!backtrace) { - populate_open_files_list(target, &open_files); - } - - // Drop our capabilities now that we've attached to the threads we care about. - drop_capabilities(); - - check_process(target_proc_fd, target); - // TODO: Use seccomp to lock ourselves down. std::string amfd_data; - if (backtrace) { dump_backtrace(output_fd.get(), backtrace_map.get(), target, main_tid, attached_siblings, 0); } else { diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp index cb8f0172f..a5de83a29 100644 --- a/debuggerd/handler/debuggerd_handler.cpp +++ b/debuggerd/handler/debuggerd_handler.cpp @@ -254,9 +254,11 @@ static int debuggerd_dispatch_pseudothread(void* arg) { raise_caps(); - char buf[10]; - snprintf(buf, sizeof(buf), "%d", thread_info->crashing_tid); - execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, buf, nullptr); + char main_tid[10]; + char pseudothread_tid[10]; + snprintf(main_tid, sizeof(main_tid), "%d", thread_info->crashing_tid); + snprintf(pseudothread_tid, sizeof(pseudothread_tid), "%d", thread_info->pseudothread_tid); + execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, nullptr); fatal_errno("exec failed"); } else { @@ -381,6 +383,12 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) .info = info }; + // Set PR_SET_DUMPABLE to 1, so that crash_dump can ptrace us. + int orig_dumpable = prctl(PR_GET_DUMPABLE); + if (prctl(PR_SET_DUMPABLE, 1) != 0) { + fatal_errno("failed to set dumpable"); + } + // Essentially pthread_create without CLONE_FILES (see debuggerd_dispatch_pseudothread). pid_t child_pid = clone(debuggerd_dispatch_pseudothread, pseudothread_stack, @@ -396,6 +404,11 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) // and then wait for it to finish. __futex_wait(&thread_info.pseudothread_tid, child_pid, nullptr); + // Restore PR_SET_DUMPABLE to its original value. + if (prctl(PR_SET_DUMPABLE, orig_dumpable) != 0) { + fatal_errno("failed to restore dumpable"); + } + // Signals can either be fatal or nonfatal. // For fatal signals, crash_dump will PTRACE_CONT us with the signal we // crashed with, so that processes using waitpid on us will see that we