diff options
author | Josh Gao <jmgao@google.com> | 2017-02-08 16:06:26 -0800 |
---|---|---|
committer | Josh Gao <jmgao@google.com> | 2017-02-15 17:03:44 -0800 |
commit | e73c932373e59e4c0351cc7a8bd8cc5b8910d87e (patch) | |
tree | b8b8a1945ab8caba4b31ad1440a055033276bf00 | |
parent | f6ad5851e689f54c9dee6bfc6668ca726726e818 (diff) | |
download | core-e73c932373e59e4c0351cc7a8bd8cc5b8910d87e.tar.gz core-e73c932373e59e4c0351cc7a8bd8cc5b8910d87e.tar.bz2 core-e73c932373e59e4c0351cc7a8bd8cc5b8910d87e.zip |
libdebuggerd_handler: in-process crash dumping for seccomped processes.
Do an in-process unwind for processes that have PR_SET_NO_NEW_PRIVS
enabled.
Bug: http://b/34684590
Test: debuggerd_test, killall -ABRT media.codec
Change-Id: I62562ec2c419d6643970100ab1cc0288982a1eed
-rw-r--r-- | debuggerd/Android.bp | 50 | ||||
-rw-r--r-- | debuggerd/crash_dump.cpp | 4 | ||||
-rw-r--r-- | debuggerd/handler/debuggerd_fallback.cpp | 48 | ||||
-rw-r--r-- | debuggerd/handler/debuggerd_fallback_nop.cpp | 35 | ||||
-rw-r--r-- | debuggerd/handler/debuggerd_handler.cpp | 22 | ||||
-rw-r--r-- | debuggerd/libdebuggerd/include/tombstone.h | 7 | ||||
-rw-r--r-- | debuggerd/libdebuggerd/tombstone.cpp | 66 |
7 files changed, 193 insertions, 39 deletions
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp index ca881aaf0..8d2ea6840 100644 --- a/debuggerd/Android.bp +++ b/debuggerd/Android.bp @@ -12,14 +12,48 @@ cc_defaults { } cc_library_static { - name: "libdebuggerd_handler", + name: "libdebuggerd_handler_core", defaults: ["debuggerd_defaults"], srcs: ["handler/debuggerd_handler.cpp"], // libdebuggerd_handler gets async signal safe logging via libc_logging, // which defines its interface in bionic private headers. include_dirs: ["bionic/libc"], - static_libs: ["libc_logging"], + whole_static_libs: [ + "libc_logging", + "libdebuggerd", + ], + + export_include_dirs: ["include"], +} + +cc_library_static { + name: "libdebuggerd_handler", + defaults: ["debuggerd_defaults"], + srcs: ["handler/debuggerd_fallback_nop.cpp"], + + whole_static_libs: [ + "libdebuggerd_handler_core", + ], + + export_include_dirs: ["include"], +} + +cc_library_static { + name: "libdebuggerd_handler_fallback", + defaults: ["debuggerd_defaults"], + srcs: ["handler/debuggerd_fallback.cpp"], + + // libdebuggerd_handler gets async signal safe logging via libc_logging, + // which defines its interface in bionic private headers. + include_dirs: ["bionic/libc"], + static_libs: [ + "libdebuggerd", + "libbacktrace", + "libunwind", + "liblzma", + "libcutils", + ], export_include_dirs: ["include"], } @@ -39,7 +73,7 @@ cc_library { export_include_dirs: ["include"], } -cc_library { +cc_library_static { name: "libdebuggerd", defaults: ["debuggerd_defaults"], @@ -75,8 +109,10 @@ cc_library { local_include_dirs: ["libdebuggerd/include"], export_include_dirs: ["libdebuggerd/include"], - shared_libs: [ + static_libs: [ "libbacktrace", + "libunwind", + "liblzma", "libbase", "libcutils", "liblog", @@ -150,10 +186,14 @@ cc_binary { }, }, + static_libs: [ + "libdebuggerd", + "libcutils", + ], + shared_libs: [ "libbacktrace", "libbase", - "libdebuggerd", "liblog", "libprocinfo", "libselinux", diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp index d4be25bd2..9d24b89fb 100644 --- a/debuggerd/crash_dump.cpp +++ b/debuggerd/crash_dump.cpp @@ -387,8 +387,8 @@ int main(int argc, char** argv) { if (backtrace) { dump_backtrace(output_fd.get(), backtrace_map.get(), target, main_tid, attached_siblings, 0); } else { - engrave_tombstone(output_fd.get(), backtrace_map.get(), open_files, target, main_tid, - attached_siblings, abort_address, fatal_signal ? &amfd_data : nullptr); + engrave_tombstone(output_fd.get(), backtrace_map.get(), &open_files, target, main_tid, + &attached_siblings, abort_address, fatal_signal ? &amfd_data : nullptr); } // We don't actually need to PTRACE_DETACH, as long as our tracees aren't in diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp new file mode 100644 index 000000000..77ad6ac1e --- /dev/null +++ b/debuggerd/handler/debuggerd_fallback.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stddef.h> +#include <sys/ucontext.h> +#include <unistd.h> + +#include "tombstone.h" + +extern "C" void __linker_use_fallback_allocator(); + +extern "C" bool debuggerd_fallback(ucontext_t* ucontext, siginfo_t* siginfo, void* abort_message) { + // This is incredibly sketchy to do inside of a signal handler, especially when libbacktrace + // uses the C++ standard library throughout, but this code runs in the linker, so we'll be using + // the linker's malloc instead of the libc one. Switch it out for a replacement, just in case. + // + // This isn't the default method of dumping because it can fail in cases such as memory space + // exhaustion. + __linker_use_fallback_allocator(); + engrave_tombstone_ucontext(-1, getpid(), gettid(), reinterpret_cast<uintptr_t>(abort_message), + siginfo, ucontext); + return true; +} diff --git a/debuggerd/handler/debuggerd_fallback_nop.cpp b/debuggerd/handler/debuggerd_fallback_nop.cpp new file mode 100644 index 000000000..9b3053f3b --- /dev/null +++ b/debuggerd/handler/debuggerd_fallback_nop.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stddef.h> +#include <sys/ucontext.h> +#include <unistd.h> + +extern "C" bool debuggerd_fallback(ucontext_t*, siginfo_t*, void*) { + return false; +} diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp index b1dc01aca..680ba4bee 100644 --- a/debuggerd/handler/debuggerd_handler.cpp +++ b/debuggerd/handler/debuggerd_handler.cpp @@ -62,6 +62,8 @@ #define CRASH_DUMP_PATH "/system/bin/" CRASH_DUMP_NAME +extern "C" bool debuggerd_fallback(ucontext_t*, siginfo_t*, void*); + static debuggerd_callbacks_t g_callbacks; // Mutex to ensure only one crashing thread dumps itself. @@ -329,7 +331,7 @@ static void resend_signal(siginfo_t* info, bool crash_dump_started) { // Handler that does crash dumping by forking and doing the processing in the child. // Do this by ptracing the relevant thread, and then execing debuggerd to do the actual dump. -static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) { +static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* context) { int ret = pthread_mutex_lock(&crash_mutex); if (ret != 0) { __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret)); @@ -359,18 +361,22 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) log_signal_summary(signal_number, info); + void* abort_message = nullptr; + if (g_callbacks.get_abort_message) { + abort_message = g_callbacks.get_abort_message(); + } + if (prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) { - // The process has NO_NEW_PRIVS enabled, so we can't transition to the crash_dump context. - __libc_format_log(ANDROID_LOG_INFO, "libc", - "Suppressing debuggerd output because prctl(PR_GET_NO_NEW_PRIVS)==1"); + ucontext_t* ucontext = static_cast<ucontext_t*>(context); + if (signal_number == DEBUGGER_SIGNAL || !debuggerd_fallback(ucontext, info, abort_message)) { + // The process has NO_NEW_PRIVS enabled, so we can't transition to the crash_dump context. + __libc_format_log(ANDROID_LOG_INFO, "libc", + "Suppressing debuggerd output because prctl(PR_GET_NO_NEW_PRIVS)==1"); + } resend_signal(info, false); return; } - void* abort_message = nullptr; - if (g_callbacks.get_abort_message) { - abort_message = g_callbacks.get_abort_message(); - } // Populate si_value with the abort message address, if found. if (abort_message) { info->si_value.sival_ptr = abort_message; diff --git a/debuggerd/libdebuggerd/include/tombstone.h b/debuggerd/libdebuggerd/include/tombstone.h index 4ff24af26..aed71de22 100644 --- a/debuggerd/libdebuggerd/include/tombstone.h +++ b/debuggerd/libdebuggerd/include/tombstone.h @@ -35,8 +35,11 @@ int open_tombstone(std::string* path); /* Creates a tombstone file and writes the crash dump to it. */ void engrave_tombstone(int tombstone_fd, BacktraceMap* map, - const OpenFilesList& open_files, pid_t pid, pid_t tid, - const std::set<pid_t>& siblings, uintptr_t abort_msg_address, + const OpenFilesList* open_files, pid_t pid, pid_t tid, + const std::set<pid_t>* siblings, uintptr_t abort_msg_address, std::string* amfd_data); +void engrave_tombstone_ucontext(int tombstone_fd, pid_t pid, pid_t tid, uintptr_t abort_msg_address, + siginfo_t* siginfo, ucontext_t* ucontext); + #endif // _DEBUGGERD_TOMBSTONE_H diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp index ac2c0b6c4..3166bfc9d 100644 --- a/debuggerd/libdebuggerd/tombstone.cpp +++ b/debuggerd/libdebuggerd/tombstone.cpp @@ -220,14 +220,8 @@ static void dump_probable_cause(log_t* log, const siginfo_t& si) { if (!cause.empty()) _LOG(log, logtype::HEADER, "Cause: %s\n", cause.c_str()); } -static void dump_signal_info(log_t* log, pid_t tid) { - siginfo_t si; - memset(&si, 0, sizeof(si)); - if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == -1) { - ALOGE("cannot get siginfo: %s\n", strerror(errno)); - return; - } - +static void dump_signal_info(log_t* log, const siginfo_t* siginfo) { + const siginfo_t& si = *siginfo; char addr_desc[32]; // ", fault addr 0x1234" if (signal_has_si_addr(si.si_signo, si.si_code)) { snprintf(addr_desc, sizeof(addr_desc), "%p", si.si_addr); @@ -241,6 +235,17 @@ static void dump_signal_info(log_t* log, pid_t tid) { dump_probable_cause(log, si); } +static void dump_signal_info(log_t* log, pid_t tid) { + siginfo_t si; + memset(&si, 0, sizeof(si)); + if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == -1) { + ALOGE("cannot get siginfo: %s\n", strerror(errno)); + return; + } + + dump_signal_info(log, &si); +} + static void dump_thread_info(log_t* log, pid_t pid, pid_t tid) { char path[64]; char threadnamebuf[1024]; @@ -649,8 +654,8 @@ static void dump_logs(log_t* log, pid_t pid, unsigned int tail) { // Dumps all information about the specified pid to the tombstone. static void dump_crash(log_t* log, BacktraceMap* map, - const OpenFilesList& open_files, pid_t pid, pid_t tid, - const std::set<pid_t>& siblings, uintptr_t abort_msg_address) { + const OpenFilesList* open_files, pid_t pid, pid_t tid, + const std::set<pid_t>* siblings, uintptr_t abort_msg_address) { // don't copy log messages to tombstone unless this is a dev device char value[PROPERTY_VALUE_MAX]; property_get("ro.debuggable", value, "0"); @@ -664,14 +669,16 @@ static void dump_crash(log_t* log, BacktraceMap* map, dump_logs(log, pid, 5); } - if (!siblings.empty()) { - for (pid_t sibling : siblings) { + if (siblings && !siblings->empty()) { + for (pid_t sibling : *siblings) { dump_thread(log, pid, sibling, map, 0, false); } } - _LOG(log, logtype::OPEN_FILES, "\nopen files:\n"); - dump_open_files_list_to_log(open_files, log, " "); + if (open_files) { + _LOG(log, logtype::OPEN_FILES, "\nopen files:\n"); + dump_open_files_list_to_log(*open_files, log, " "); + } if (want_logs) { dump_logs(log, pid, 0); @@ -732,19 +739,34 @@ int open_tombstone(std::string* out_path) { } void engrave_tombstone(int tombstone_fd, BacktraceMap* map, - const OpenFilesList& open_files, pid_t pid, pid_t tid, - const std::set<pid_t>& siblings, uintptr_t abort_msg_address, + const OpenFilesList* open_files, pid_t pid, pid_t tid, + const std::set<pid_t>* siblings, uintptr_t abort_msg_address, std::string* amfd_data) { log_t log; log.current_tid = tid; log.crashed_tid = tid; - - if (tombstone_fd < 0) { - ALOGE("debuggerd: skipping tombstone write, nothing to do.\n"); - return; - } - log.tfd = tombstone_fd; log.amfd_data = amfd_data; dump_crash(&log, map, open_files, pid, tid, siblings, abort_msg_address); } + +void engrave_tombstone_ucontext(int tombstone_fd, pid_t pid, pid_t tid, uintptr_t abort_msg_address, + siginfo_t* siginfo, ucontext_t* ucontext) { + log_t log; + log.current_tid = tid; + log.crashed_tid = tid; + log.tfd = tombstone_fd; + log.amfd_data = nullptr; + + dump_thread_info(&log, pid, tid); + dump_signal_info(&log, siginfo); + + std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid)); + dump_abort_message(backtrace.get(), &log, abort_msg_address); + // TODO: Dump registers from the ucontext. + if (backtrace->Unwind(0, ucontext)) { + dump_backtrace_and_stack(backtrace.get(), &log); + } else { + ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid); + } +} |