Loading debuggerd/Android.bp +32 −8 Original line number Original line Diff line number Diff line Loading @@ -8,17 +8,35 @@ cc_defaults { "-Os", "-Os", ], ], // util.cpp gets async signal safe logging via libc_logging, // which defines its interface in bionic private headers. include_dirs: ["bionic/libc"], local_include_dirs: ["include"], local_include_dirs: ["include"], } } // Utility library to tombstoned and get an output fd. cc_library_static { name: "libtombstoned_client", defaults: ["debuggerd_defaults"], srcs: [ "tombstoned_client.cpp", "util.cpp", ], whole_static_libs: [ "libc_logging", "libcutils", "libbase", ], } // Core implementation, linked into libdebuggerd_handler and the dynamic linker. cc_library_static { cc_library_static { name: "libdebuggerd_handler_core", name: "libdebuggerd_handler_core", defaults: ["debuggerd_defaults"], defaults: ["debuggerd_defaults"], srcs: ["handler/debuggerd_handler.cpp"], 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"], whole_static_libs: [ whole_static_libs: [ "libc_logging", "libc_logging", "libdebuggerd", "libdebuggerd", Loading @@ -27,6 +45,7 @@ cc_library_static { export_include_dirs: ["include"], export_include_dirs: ["include"], } } // Implementation with a no-op fallback. cc_library_static { cc_library_static { name: "libdebuggerd_handler", name: "libdebuggerd_handler", defaults: ["debuggerd_defaults"], defaults: ["debuggerd_defaults"], Loading @@ -39,15 +58,18 @@ cc_library_static { export_include_dirs: ["include"], export_include_dirs: ["include"], } } // Fallback implementation. cc_library_static { cc_library_static { name: "libdebuggerd_handler_fallback", name: "libdebuggerd_handler_fallback", defaults: ["debuggerd_defaults"], defaults: ["debuggerd_defaults"], srcs: ["handler/debuggerd_fallback.cpp"], srcs: [ "handler/debuggerd_fallback.cpp", ], // libdebuggerd_handler gets async signal safe logging via libc_logging, whole_static_libs: [ // which defines its interface in bionic private headers. "libdebuggerd_handler_core", include_dirs: ["bionic/libc"], "libtombstoned_client", static_libs: [ "libbase", "libdebuggerd", "libdebuggerd", "libbacktrace", "libbacktrace", "libunwind", "libunwind", Loading @@ -70,6 +92,7 @@ cc_library { "libbase", "libbase", "libcutils", "libcutils", ], ], export_include_dirs: ["include"], export_include_dirs: ["include"], } } Loading Loading @@ -187,6 +210,7 @@ cc_binary { }, }, static_libs: [ static_libs: [ "libtombstoned_client", "libdebuggerd", "libdebuggerd", "libcutils", "libcutils", ], ], Loading debuggerd/crash_dump.cpp +1 −49 Original line number Original line Diff line number Diff line Loading @@ -48,6 +48,7 @@ #include "debuggerd/handler.h" #include "debuggerd/handler.h" #include "debuggerd/protocol.h" #include "debuggerd/protocol.h" #include "debuggerd/tombstoned.h" #include "debuggerd/util.h" #include "debuggerd/util.h" using android::base::unique_fd; using android::base::unique_fd; Loading Loading @@ -128,55 +129,6 @@ static bool activity_manager_notify(int pid, int signal, const std::string& amfd return true; return true; } } static bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd) { unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET)); if (sockfd == -1) { PLOG(ERROR) << "failed to connect to tombstoned"; return false; } TombstonedCrashPacket packet = {}; packet.packet_type = CrashPacketType::kDumpRequest; packet.packet.dump_request.pid = pid; if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) { PLOG(ERROR) << "failed to write DumpRequest packet"; return false; } unique_fd tmp_output_fd; ssize_t rc = recv_fd(sockfd, &packet, sizeof(packet), &tmp_output_fd); if (rc == -1) { PLOG(ERROR) << "failed to read response to DumpRequest packet"; return false; } else if (rc != sizeof(packet)) { LOG(ERROR) << "read DumpRequest response packet of incorrect length (expected " << sizeof(packet) << ", got " << rc << ")"; return false; } // Make the fd O_APPEND so that our output is guaranteed to be at the end of a file. // (This also makes selinux rules consistent, because selinux distinguishes between writing to // a regular fd, and writing to an fd with O_APPEND). int flags = fcntl(tmp_output_fd.get(), F_GETFL); if (fcntl(tmp_output_fd.get(), F_SETFL, flags | O_APPEND) != 0) { PLOG(WARNING) << "failed to set output fd flags"; } *tombstoned_socket = std::move(sockfd); *output_fd = std::move(tmp_output_fd); return true; } static bool tombstoned_notify_completion(int tombstoned_socket) { TombstonedCrashPacket packet = {}; packet.packet_type = CrashPacketType::kCompletedDump; if (TEMP_FAILURE_RETRY(write(tombstoned_socket, &packet, sizeof(packet))) != sizeof(packet)) { return false; } return true; } static void signal_handler(int) { static void signal_handler(int) { // We can't log easily, because the heap might be corrupt. // We can't log easily, because the heap might be corrupt. // Just die and let the surrounding log context explain things. // Just die and let the surrounding log context explain things. Loading debuggerd/handler/debuggerd_fallback.cpp +196 −13 Original line number Original line Diff line number Diff line Loading @@ -26,23 +26,206 @@ * SUCH DAMAGE. * SUCH DAMAGE. */ */ #include <dirent.h> #include <fcntl.h> #include <poll.h> #include <pthread.h> #include <stddef.h> #include <stddef.h> #include <sys/ucontext.h> #include <sys/ucontext.h> #include <syscall.h> #include <unistd.h> #include <unistd.h> #include <atomic> #include <android-base/file.h> #include <android-base/unique_fd.h> #include "debuggerd/handler.h" #include "debuggerd/tombstoned.h" #include "debuggerd/util.h" #include "backtrace.h" #include "tombstone.h" #include "tombstone.h" extern "C" void __linker_use_fallback_allocator(); #include "private/libc_logging.h" using android::base::unique_fd; extern "C" void __linker_enable_fallback_allocator(); extern "C" void __linker_disable_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 // 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 // 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. // 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 // This isn't the default method of dumping because it can fail in cases such as address space // exhaustion. // exhaustion. __linker_use_fallback_allocator(); static void debuggerd_fallback_trace(int output_fd, ucontext_t* ucontext) { engrave_tombstone_ucontext(-1, getpid(), gettid(), reinterpret_cast<uintptr_t>(abort_message), __linker_enable_fallback_allocator(); siginfo, ucontext); dump_backtrace_ucontext(output_fd, ucontext); __linker_disable_fallback_allocator(); } static void debuggerd_fallback_tombstone(int output_fd, ucontext_t* ucontext, siginfo_t* siginfo, void* abort_message) { __linker_enable_fallback_allocator(); engrave_tombstone_ucontext(output_fd, reinterpret_cast<uintptr_t>(abort_message), siginfo, ucontext); __linker_disable_fallback_allocator(); } static void iterate_siblings(bool (*callback)(pid_t, int), int output_fd) { pid_t current_tid = gettid(); char buf[BUFSIZ]; snprintf(buf, sizeof(buf), "/proc/%d/task", current_tid); DIR* dir = opendir(buf); if (!dir) { __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to open %s: %s", buf, strerror(errno)); return; } struct dirent* ent; while ((ent = readdir(dir))) { char* end; long tid = strtol(ent->d_name, &end, 10); if (end == ent->d_name || *end != '\0') { continue; } if (tid != current_tid) { callback(tid, output_fd); } } closedir(dir); } static bool forward_output(int src_fd, int dst_fd) { // Make sure the thread actually got the signal. struct pollfd pfd = { .fd = src_fd, .events = POLLIN, }; // Wait for up to a second for output to start flowing. if (poll(&pfd, 1, 1000) != 1) { return false; } while (true) { char buf[512]; ssize_t rc = TEMP_FAILURE_RETRY(read(src_fd, buf, sizeof(buf))); if (rc == 0) { return true; } else if (rc < 0) { return false; } if (!android::base::WriteFully(dst_fd, buf, rc)) { // We failed to write to tombstoned, but there's not much we can do. // Keep reading from src_fd to keep things going. continue; } } } static void trace_handler(siginfo_t* info, ucontext_t* ucontext) { static std::atomic<int> trace_output_fd(-1); if (info->si_value.sival_int == ~0) { // Asked to dump by the original signal recipient. debuggerd_fallback_trace(trace_output_fd, ucontext); int tmp = trace_output_fd.load(); trace_output_fd.store(-1); close(tmp); return; } // Only allow one thread to perform a trace at a time. static pthread_mutex_t trace_mutex = PTHREAD_MUTEX_INITIALIZER; int ret = pthread_mutex_trylock(&trace_mutex); if (ret != 0) { __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_try_lock failed: %s", strerror(ret)); return; } // Fetch output fd from tombstoned. unique_fd tombstone_socket, output_fd; if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd)) { goto exit; } dump_backtrace_header(output_fd.get()); // Dump our own stack. debuggerd_fallback_trace(output_fd.get(), ucontext); // Send a signal to all of our siblings, asking them to dump their stack. iterate_siblings( [](pid_t tid, int output_fd) { // Use a pipe, to be able to detect situations where the thread gracefully exits before // receiving our signal. unique_fd pipe_read, pipe_write; if (!Pipe(&pipe_read, &pipe_write)) { __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s", strerror(errno)); return false; } trace_output_fd.store(pipe_write.get()); siginfo_t siginfo = {}; siginfo.si_code = SI_QUEUE; siginfo.si_value.sival_int = ~0; siginfo.si_pid = getpid(); siginfo.si_uid = getuid(); if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, DEBUGGER_SIGNAL, &siginfo) != 0) { __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s", tid, strerror(errno)); return false; } bool success = forward_output(pipe_read.get(), output_fd); if (success) { // The signaled thread has closed trace_output_fd already. (void)pipe_write.release(); } else { trace_output_fd.store(-1); } return true; return true; }, output_fd.get()); dump_backtrace_footer(output_fd.get()); tombstoned_notify_completion(tombstone_socket.get()); exit: pthread_mutex_unlock(&trace_mutex); } static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) { // Only allow one thread to handle a crash. static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER; int ret = pthread_mutex_lock(&crash_mutex); if (ret != 0) { __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret)); return; } unique_fd tombstone_socket, output_fd; bool tombstoned_connected = tombstoned_connect(getpid(), &tombstone_socket, &output_fd); debuggerd_fallback_tombstone(output_fd.get(), ucontext, info, abort_message); if (tombstoned_connected) { tombstoned_notify_completion(tombstone_socket.get()); } } extern "C" void debuggerd_fallback_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) { if (info->si_signo == DEBUGGER_SIGNAL) { return trace_handler(info, ucontext); } else { return crash_handler(info, ucontext, abort_message); } } } debuggerd/handler/debuggerd_fallback_nop.cpp +1 −6 Original line number Original line Diff line number Diff line Loading @@ -26,10 +26,5 @@ * SUCH DAMAGE. * SUCH DAMAGE. */ */ #include <stddef.h> extern "C" void debuggerd_fallback_handler(struct siginfo_t*, struct ucontext_t*, void*) { #include <sys/ucontext.h> #include <unistd.h> extern "C" bool debuggerd_fallback(ucontext_t*, siginfo_t*, void*) { return false; } } debuggerd/handler/debuggerd_handler.cpp +19 −19 Original line number Original line Diff line number Diff line Loading @@ -62,7 +62,7 @@ #define CRASH_DUMP_PATH "/system/bin/" CRASH_DUMP_NAME #define CRASH_DUMP_PATH "/system/bin/" CRASH_DUMP_NAME extern "C" bool debuggerd_fallback(ucontext_t*, siginfo_t*, void*); extern "C" void debuggerd_fallback_handler(siginfo_t*, ucontext_t*, void*); static debuggerd_callbacks_t g_callbacks; static debuggerd_callbacks_t g_callbacks; Loading Loading @@ -323,21 +323,11 @@ static void resend_signal(siginfo_t* info, bool crash_dump_started) { fatal_errno("failed to resend signal during crash"); fatal_errno("failed to resend signal during crash"); } } } } if (info->si_signo == DEBUGGER_SIGNAL) { pthread_mutex_unlock(&crash_mutex); } } } // Handler that does crash dumping by forking and doing the processing in the child. // 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. // 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* context) { 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)); return; } // It's possible somebody cleared the SA_SIGINFO flag, which would mean // It's possible somebody cleared the SA_SIGINFO flag, which would mean // our "info" arg holds an undefined value. // our "info" arg holds an undefined value. if (!have_siginfo(signal_number)) { if (!have_siginfo(signal_number)) { Loading @@ -359,24 +349,29 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c // check to allow all si_code values in calls coming from inside the house. // check to allow all si_code values in calls coming from inside the house. } } log_signal_summary(signal_number, info); void* abort_message = nullptr; void* abort_message = nullptr; if (g_callbacks.get_abort_message) { if (g_callbacks.get_abort_message) { abort_message = g_callbacks.get_abort_message(); abort_message = g_callbacks.get_abort_message(); } } if (prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) { if (prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) { ucontext_t* ucontext = static_cast<ucontext_t*>(context); // This check might be racy if another thread sets NO_NEW_PRIVS, but this should be unlikely, if (signal_number == DEBUGGER_SIGNAL || !debuggerd_fallback(ucontext, info, abort_message)) { // you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing // The process has NO_NEW_PRIVS enabled, so we can't transition to the crash_dump context. // ANR trace. __libc_format_log(ANDROID_LOG_INFO, "libc", debuggerd_fallback_handler(info, static_cast<ucontext_t*>(context), abort_message); "Suppressing debuggerd output because prctl(PR_GET_NO_NEW_PRIVS)==1"); } resend_signal(info, false); resend_signal(info, false); return; return; } } // Only allow one thread to handle a signal at a time. int ret = pthread_mutex_lock(&crash_mutex); if (ret != 0) { __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret)); return; } log_signal_summary(signal_number, info); // Populate si_value with the abort message address, if found. // Populate si_value with the abort message address, if found. if (abort_message) { if (abort_message) { info->si_value.sival_ptr = abort_message; info->si_value.sival_ptr = abort_message; Loading Loading @@ -427,6 +422,11 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c } } resend_signal(info, thread_info.crash_dump_started); resend_signal(info, thread_info.crash_dump_started); if (info->si_signo == DEBUGGER_SIGNAL) { // If the signal is fatal, don't unlock the mutex to prevent other crashing threads from // starting to dump right before our death. pthread_mutex_unlock(&crash_mutex); } } } void debuggerd_init(debuggerd_callbacks_t* callbacks) { void debuggerd_init(debuggerd_callbacks_t* callbacks) { Loading Loading
debuggerd/Android.bp +32 −8 Original line number Original line Diff line number Diff line Loading @@ -8,17 +8,35 @@ cc_defaults { "-Os", "-Os", ], ], // util.cpp gets async signal safe logging via libc_logging, // which defines its interface in bionic private headers. include_dirs: ["bionic/libc"], local_include_dirs: ["include"], local_include_dirs: ["include"], } } // Utility library to tombstoned and get an output fd. cc_library_static { name: "libtombstoned_client", defaults: ["debuggerd_defaults"], srcs: [ "tombstoned_client.cpp", "util.cpp", ], whole_static_libs: [ "libc_logging", "libcutils", "libbase", ], } // Core implementation, linked into libdebuggerd_handler and the dynamic linker. cc_library_static { cc_library_static { name: "libdebuggerd_handler_core", name: "libdebuggerd_handler_core", defaults: ["debuggerd_defaults"], defaults: ["debuggerd_defaults"], srcs: ["handler/debuggerd_handler.cpp"], 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"], whole_static_libs: [ whole_static_libs: [ "libc_logging", "libc_logging", "libdebuggerd", "libdebuggerd", Loading @@ -27,6 +45,7 @@ cc_library_static { export_include_dirs: ["include"], export_include_dirs: ["include"], } } // Implementation with a no-op fallback. cc_library_static { cc_library_static { name: "libdebuggerd_handler", name: "libdebuggerd_handler", defaults: ["debuggerd_defaults"], defaults: ["debuggerd_defaults"], Loading @@ -39,15 +58,18 @@ cc_library_static { export_include_dirs: ["include"], export_include_dirs: ["include"], } } // Fallback implementation. cc_library_static { cc_library_static { name: "libdebuggerd_handler_fallback", name: "libdebuggerd_handler_fallback", defaults: ["debuggerd_defaults"], defaults: ["debuggerd_defaults"], srcs: ["handler/debuggerd_fallback.cpp"], srcs: [ "handler/debuggerd_fallback.cpp", ], // libdebuggerd_handler gets async signal safe logging via libc_logging, whole_static_libs: [ // which defines its interface in bionic private headers. "libdebuggerd_handler_core", include_dirs: ["bionic/libc"], "libtombstoned_client", static_libs: [ "libbase", "libdebuggerd", "libdebuggerd", "libbacktrace", "libbacktrace", "libunwind", "libunwind", Loading @@ -70,6 +92,7 @@ cc_library { "libbase", "libbase", "libcutils", "libcutils", ], ], export_include_dirs: ["include"], export_include_dirs: ["include"], } } Loading Loading @@ -187,6 +210,7 @@ cc_binary { }, }, static_libs: [ static_libs: [ "libtombstoned_client", "libdebuggerd", "libdebuggerd", "libcutils", "libcutils", ], ], Loading
debuggerd/crash_dump.cpp +1 −49 Original line number Original line Diff line number Diff line Loading @@ -48,6 +48,7 @@ #include "debuggerd/handler.h" #include "debuggerd/handler.h" #include "debuggerd/protocol.h" #include "debuggerd/protocol.h" #include "debuggerd/tombstoned.h" #include "debuggerd/util.h" #include "debuggerd/util.h" using android::base::unique_fd; using android::base::unique_fd; Loading Loading @@ -128,55 +129,6 @@ static bool activity_manager_notify(int pid, int signal, const std::string& amfd return true; return true; } } static bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd) { unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET)); if (sockfd == -1) { PLOG(ERROR) << "failed to connect to tombstoned"; return false; } TombstonedCrashPacket packet = {}; packet.packet_type = CrashPacketType::kDumpRequest; packet.packet.dump_request.pid = pid; if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) { PLOG(ERROR) << "failed to write DumpRequest packet"; return false; } unique_fd tmp_output_fd; ssize_t rc = recv_fd(sockfd, &packet, sizeof(packet), &tmp_output_fd); if (rc == -1) { PLOG(ERROR) << "failed to read response to DumpRequest packet"; return false; } else if (rc != sizeof(packet)) { LOG(ERROR) << "read DumpRequest response packet of incorrect length (expected " << sizeof(packet) << ", got " << rc << ")"; return false; } // Make the fd O_APPEND so that our output is guaranteed to be at the end of a file. // (This also makes selinux rules consistent, because selinux distinguishes between writing to // a regular fd, and writing to an fd with O_APPEND). int flags = fcntl(tmp_output_fd.get(), F_GETFL); if (fcntl(tmp_output_fd.get(), F_SETFL, flags | O_APPEND) != 0) { PLOG(WARNING) << "failed to set output fd flags"; } *tombstoned_socket = std::move(sockfd); *output_fd = std::move(tmp_output_fd); return true; } static bool tombstoned_notify_completion(int tombstoned_socket) { TombstonedCrashPacket packet = {}; packet.packet_type = CrashPacketType::kCompletedDump; if (TEMP_FAILURE_RETRY(write(tombstoned_socket, &packet, sizeof(packet))) != sizeof(packet)) { return false; } return true; } static void signal_handler(int) { static void signal_handler(int) { // We can't log easily, because the heap might be corrupt. // We can't log easily, because the heap might be corrupt. // Just die and let the surrounding log context explain things. // Just die and let the surrounding log context explain things. Loading
debuggerd/handler/debuggerd_fallback.cpp +196 −13 Original line number Original line Diff line number Diff line Loading @@ -26,23 +26,206 @@ * SUCH DAMAGE. * SUCH DAMAGE. */ */ #include <dirent.h> #include <fcntl.h> #include <poll.h> #include <pthread.h> #include <stddef.h> #include <stddef.h> #include <sys/ucontext.h> #include <sys/ucontext.h> #include <syscall.h> #include <unistd.h> #include <unistd.h> #include <atomic> #include <android-base/file.h> #include <android-base/unique_fd.h> #include "debuggerd/handler.h" #include "debuggerd/tombstoned.h" #include "debuggerd/util.h" #include "backtrace.h" #include "tombstone.h" #include "tombstone.h" extern "C" void __linker_use_fallback_allocator(); #include "private/libc_logging.h" using android::base::unique_fd; extern "C" void __linker_enable_fallback_allocator(); extern "C" void __linker_disable_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 // 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 // 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. // 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 // This isn't the default method of dumping because it can fail in cases such as address space // exhaustion. // exhaustion. __linker_use_fallback_allocator(); static void debuggerd_fallback_trace(int output_fd, ucontext_t* ucontext) { engrave_tombstone_ucontext(-1, getpid(), gettid(), reinterpret_cast<uintptr_t>(abort_message), __linker_enable_fallback_allocator(); siginfo, ucontext); dump_backtrace_ucontext(output_fd, ucontext); __linker_disable_fallback_allocator(); } static void debuggerd_fallback_tombstone(int output_fd, ucontext_t* ucontext, siginfo_t* siginfo, void* abort_message) { __linker_enable_fallback_allocator(); engrave_tombstone_ucontext(output_fd, reinterpret_cast<uintptr_t>(abort_message), siginfo, ucontext); __linker_disable_fallback_allocator(); } static void iterate_siblings(bool (*callback)(pid_t, int), int output_fd) { pid_t current_tid = gettid(); char buf[BUFSIZ]; snprintf(buf, sizeof(buf), "/proc/%d/task", current_tid); DIR* dir = opendir(buf); if (!dir) { __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to open %s: %s", buf, strerror(errno)); return; } struct dirent* ent; while ((ent = readdir(dir))) { char* end; long tid = strtol(ent->d_name, &end, 10); if (end == ent->d_name || *end != '\0') { continue; } if (tid != current_tid) { callback(tid, output_fd); } } closedir(dir); } static bool forward_output(int src_fd, int dst_fd) { // Make sure the thread actually got the signal. struct pollfd pfd = { .fd = src_fd, .events = POLLIN, }; // Wait for up to a second for output to start flowing. if (poll(&pfd, 1, 1000) != 1) { return false; } while (true) { char buf[512]; ssize_t rc = TEMP_FAILURE_RETRY(read(src_fd, buf, sizeof(buf))); if (rc == 0) { return true; } else if (rc < 0) { return false; } if (!android::base::WriteFully(dst_fd, buf, rc)) { // We failed to write to tombstoned, but there's not much we can do. // Keep reading from src_fd to keep things going. continue; } } } static void trace_handler(siginfo_t* info, ucontext_t* ucontext) { static std::atomic<int> trace_output_fd(-1); if (info->si_value.sival_int == ~0) { // Asked to dump by the original signal recipient. debuggerd_fallback_trace(trace_output_fd, ucontext); int tmp = trace_output_fd.load(); trace_output_fd.store(-1); close(tmp); return; } // Only allow one thread to perform a trace at a time. static pthread_mutex_t trace_mutex = PTHREAD_MUTEX_INITIALIZER; int ret = pthread_mutex_trylock(&trace_mutex); if (ret != 0) { __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_try_lock failed: %s", strerror(ret)); return; } // Fetch output fd from tombstoned. unique_fd tombstone_socket, output_fd; if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd)) { goto exit; } dump_backtrace_header(output_fd.get()); // Dump our own stack. debuggerd_fallback_trace(output_fd.get(), ucontext); // Send a signal to all of our siblings, asking them to dump their stack. iterate_siblings( [](pid_t tid, int output_fd) { // Use a pipe, to be able to detect situations where the thread gracefully exits before // receiving our signal. unique_fd pipe_read, pipe_write; if (!Pipe(&pipe_read, &pipe_write)) { __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s", strerror(errno)); return false; } trace_output_fd.store(pipe_write.get()); siginfo_t siginfo = {}; siginfo.si_code = SI_QUEUE; siginfo.si_value.sival_int = ~0; siginfo.si_pid = getpid(); siginfo.si_uid = getuid(); if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, DEBUGGER_SIGNAL, &siginfo) != 0) { __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s", tid, strerror(errno)); return false; } bool success = forward_output(pipe_read.get(), output_fd); if (success) { // The signaled thread has closed trace_output_fd already. (void)pipe_write.release(); } else { trace_output_fd.store(-1); } return true; return true; }, output_fd.get()); dump_backtrace_footer(output_fd.get()); tombstoned_notify_completion(tombstone_socket.get()); exit: pthread_mutex_unlock(&trace_mutex); } static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) { // Only allow one thread to handle a crash. static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER; int ret = pthread_mutex_lock(&crash_mutex); if (ret != 0) { __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret)); return; } unique_fd tombstone_socket, output_fd; bool tombstoned_connected = tombstoned_connect(getpid(), &tombstone_socket, &output_fd); debuggerd_fallback_tombstone(output_fd.get(), ucontext, info, abort_message); if (tombstoned_connected) { tombstoned_notify_completion(tombstone_socket.get()); } } extern "C" void debuggerd_fallback_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) { if (info->si_signo == DEBUGGER_SIGNAL) { return trace_handler(info, ucontext); } else { return crash_handler(info, ucontext, abort_message); } } }
debuggerd/handler/debuggerd_fallback_nop.cpp +1 −6 Original line number Original line Diff line number Diff line Loading @@ -26,10 +26,5 @@ * SUCH DAMAGE. * SUCH DAMAGE. */ */ #include <stddef.h> extern "C" void debuggerd_fallback_handler(struct siginfo_t*, struct ucontext_t*, void*) { #include <sys/ucontext.h> #include <unistd.h> extern "C" bool debuggerd_fallback(ucontext_t*, siginfo_t*, void*) { return false; } }
debuggerd/handler/debuggerd_handler.cpp +19 −19 Original line number Original line Diff line number Diff line Loading @@ -62,7 +62,7 @@ #define CRASH_DUMP_PATH "/system/bin/" CRASH_DUMP_NAME #define CRASH_DUMP_PATH "/system/bin/" CRASH_DUMP_NAME extern "C" bool debuggerd_fallback(ucontext_t*, siginfo_t*, void*); extern "C" void debuggerd_fallback_handler(siginfo_t*, ucontext_t*, void*); static debuggerd_callbacks_t g_callbacks; static debuggerd_callbacks_t g_callbacks; Loading Loading @@ -323,21 +323,11 @@ static void resend_signal(siginfo_t* info, bool crash_dump_started) { fatal_errno("failed to resend signal during crash"); fatal_errno("failed to resend signal during crash"); } } } } if (info->si_signo == DEBUGGER_SIGNAL) { pthread_mutex_unlock(&crash_mutex); } } } // Handler that does crash dumping by forking and doing the processing in the child. // 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. // 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* context) { 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)); return; } // It's possible somebody cleared the SA_SIGINFO flag, which would mean // It's possible somebody cleared the SA_SIGINFO flag, which would mean // our "info" arg holds an undefined value. // our "info" arg holds an undefined value. if (!have_siginfo(signal_number)) { if (!have_siginfo(signal_number)) { Loading @@ -359,24 +349,29 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c // check to allow all si_code values in calls coming from inside the house. // check to allow all si_code values in calls coming from inside the house. } } log_signal_summary(signal_number, info); void* abort_message = nullptr; void* abort_message = nullptr; if (g_callbacks.get_abort_message) { if (g_callbacks.get_abort_message) { abort_message = g_callbacks.get_abort_message(); abort_message = g_callbacks.get_abort_message(); } } if (prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) { if (prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) { ucontext_t* ucontext = static_cast<ucontext_t*>(context); // This check might be racy if another thread sets NO_NEW_PRIVS, but this should be unlikely, if (signal_number == DEBUGGER_SIGNAL || !debuggerd_fallback(ucontext, info, abort_message)) { // you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing // The process has NO_NEW_PRIVS enabled, so we can't transition to the crash_dump context. // ANR trace. __libc_format_log(ANDROID_LOG_INFO, "libc", debuggerd_fallback_handler(info, static_cast<ucontext_t*>(context), abort_message); "Suppressing debuggerd output because prctl(PR_GET_NO_NEW_PRIVS)==1"); } resend_signal(info, false); resend_signal(info, false); return; return; } } // Only allow one thread to handle a signal at a time. int ret = pthread_mutex_lock(&crash_mutex); if (ret != 0) { __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret)); return; } log_signal_summary(signal_number, info); // Populate si_value with the abort message address, if found. // Populate si_value with the abort message address, if found. if (abort_message) { if (abort_message) { info->si_value.sival_ptr = abort_message; info->si_value.sival_ptr = abort_message; Loading Loading @@ -427,6 +422,11 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c } } resend_signal(info, thread_info.crash_dump_started); resend_signal(info, thread_info.crash_dump_started); if (info->si_signo == DEBUGGER_SIGNAL) { // If the signal is fatal, don't unlock the mutex to prevent other crashing threads from // starting to dump right before our death. pthread_mutex_unlock(&crash_mutex); } } } void debuggerd_init(debuggerd_callbacks_t* callbacks) { void debuggerd_init(debuggerd_callbacks_t* callbacks) { Loading