From b7e289ed028ee73fda4c044e07d3ec6f783a0ef4 Mon Sep 17 00:00:00 2001 From: Elliott Hughes <enh@google.com> Date: Fri, 25 Apr 2014 16:02:00 -0700 Subject: [PATCH] Pass si_code through to debuggerd. Because we re-raise various signals, we corrupt the si_code that debuggerd sees when it ptraces our siginfo. One possible solution (shown here) is to pass the original si_code value in the message we send to debuggerd. Change-Id: I76f9aa2c0442e5cab611d132532409e700383907 --- linker/debugger.cpp | 78 ++++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/linker/debugger.cpp b/linker/debugger.cpp index a44380cb9..272c16a23 100644 --- a/linker/debugger.cpp +++ b/linker/debugger.cpp @@ -64,6 +64,9 @@ struct debugger_msg_t { // version 2 added: uintptr_t abort_msg_address; + + // version 3 added: + int32_t original_si_code; }; // see man(2) prctl, specifically the section about PR_GET_NAME @@ -199,11 +202,44 @@ static bool have_siginfo(int signum) { return result; } +static void send_debuggerd_packet(siginfo_t* info) { + int s = socket_abstract_client(DEBUGGER_SOCKET_NAME, SOCK_STREAM); + if (s == -1) { + __libc_format_log(ANDROID_LOG_FATAL, "libc", "Unable to open connection to debuggerd: %s", + strerror(errno)); + return; + } + + // debuggerd knows our pid from the credentials on the + // local socket but we need to tell it the tid of the crashing thread. + // debuggerd will be paranoid and verify that we sent a tid + // that's actually in our process. + debugger_msg_t msg; + msg.action = DEBUGGER_ACTION_CRASH; + msg.tid = gettid(); + msg.abort_msg_address = reinterpret_cast<uintptr_t>(gAbortMessage); + msg.original_si_code = (info != NULL) ? info->si_code : 0; + int ret = TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg))); + if (ret == sizeof(msg)) { + char debuggerd_ack; + ret = TEMP_FAILURE_RETRY(read(s, &debuggerd_ack, 1)); + int saved_errno = errno; + notify_gdb_of_libraries(); + errno = saved_errno; + } else { + // read or write failed -- broken connection? + __libc_format_log(ANDROID_LOG_FATAL, "libc", "Failed while talking to debuggerd: %s", + strerror(errno)); + } + + close(s); +} + /* * Catches fatal signals so we can ask debuggerd to ptrace us before * we crash. */ -void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) { +static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) { // It's possible somebody cleared the SA_SIGINFO flag, which would mean // our "info" arg holds an undefined value. if (!have_siginfo(signal_number)) { @@ -212,38 +248,7 @@ void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) { log_signal_summary(signal_number, info); - int s = socket_abstract_client(DEBUGGER_SOCKET_NAME, SOCK_STREAM); - if (s != -1) { - // debuggerd knows our pid from the credentials on the - // local socket but we need to tell it the tid of the crashing thread. - // debuggerd will be paranoid and verify that we sent a tid - // that's actually in our process. - debugger_msg_t msg; - msg.action = DEBUGGER_ACTION_CRASH; - msg.tid = gettid(); - msg.abort_msg_address = reinterpret_cast<uintptr_t>(gAbortMessage); - int ret = TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg))); - if (ret == sizeof(msg)) { - // If the write failed, there is no point trying to read a response. - char debuggerd_ack; - ret = TEMP_FAILURE_RETRY(read(s, &debuggerd_ack, 1)); - int saved_errno = errno; - notify_gdb_of_libraries(); - errno = saved_errno; - } - - if (ret < 0) { - // read or write failed -- broken connection? - __libc_format_log(ANDROID_LOG_FATAL, "libc", "Failed while talking to debuggerd: %s", - strerror(errno)); - } - - close(s); - } else { - // socket failed; maybe process ran out of fds? - __libc_format_log(ANDROID_LOG_FATAL, "libc", "Unable to open connection to debuggerd: %s", - strerror(errno)); - } + send_debuggerd_packet(info); // Remove our net so we fault for real when we return. signal(signal_number, SIG_DFL); @@ -251,8 +256,9 @@ void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) { // These signals are not re-thrown when we resume. This means that // crashing due to (say) SIGPIPE doesn't work the way you'd expect it // to. We work around this by throwing them manually. We don't want - // to do this for *all* signals because it'll screw up the address for - // faults like SIGSEGV. + // to do this for *all* signals because it'll screw up the si_addr for + // faults like SIGSEGV. It does screw up the si_code, which is why we + // passed that to debuggerd above. switch (signal_number) { case SIGABRT: case SIGFPE: @@ -267,7 +273,7 @@ void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) { } } -void debuggerd_init() { +__LIBC_HIDDEN__ void debuggerd_init() { struct sigaction action; memset(&action, 0, sizeof(action)); sigemptyset(&action.sa_mask);