// This is the crash handler. // Does a best effort at logging and calls _exit to terminate // the process immediately (without atexit handlers, etc.). void __stack_chk_fail() { // Immediately block all (but SIGABRT) signal handlers from running code. sigset_t sigmask; sigfillset(&sigmask); sigdelset(&sigmask, SIGABRT); sigprocmask(SIG_BLOCK, &sigmask, NULL); // Use /proc/self/exe link to obtain the program name for logging // purposes. If it's not available, we set it to "<unknown>". char path[PATH_MAX]; int count; if ((count = readlink("/proc/self/exe", path, sizeof(path) - 1)) == -1) { strlcpy(path, "<unknown>", sizeof(path)); } else { path[count] = '\0'; } // Do a best effort at logging. __libc_android_log_write(ANDROID_LOG_FATAL, path, "stack corruption detected: aborted"); // Make sure there is no default action for SIGABRT. struct sigaction sa; memset(&sa, 0, sizeof(sa)); sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = SIG_DFL; sigaction(SIGABRT, &sa, NULL); // Terminate the process and exit immediately. kill(getpid(), SIGABRT); _exit(127); }
/* The functions below are not designed to be called from a heap panic * function or from a signal handler. As such, they are free to use complex * C library functions like vsnprintf() */ __LIBC_HIDDEN__ int __libc_android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap) { char buf[LOG_BUF_SIZE]; vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); return __libc_android_log_write(prio, tag, buf); }
/* * Returns true if the handler for signal "signum" has SA_SIGINFO set. */ static bool haveSiginfo(int signum) { struct sigaction oldact, newact; memset(&newact, 0, sizeof(newact)); newact.sa_handler = SIG_DFL; newact.sa_flags = SA_RESTART; sigemptyset(&newact.sa_mask); if (sigaction(signum, &newact, &oldact) < 0) { __libc_android_log_write(ANDROID_LOG_FATAL, "libc", "Failed testing for SA_SIGINFO"); return 0; } bool ret = (oldact.sa_flags & SA_SIGINFO) != 0; if (sigaction(signum, &oldact, NULL) < 0) { __libc_android_log_write(ANDROID_LOG_FATAL, "libc", "Restore failed in test for SA_SIGINFO"); } return ret; }
__LIBC_HIDDEN__ int __libc_android_log_print(int prio, const char *tag, const char *fmt, ...) { va_list ap; char buf[LOG_BUF_SIZE]; va_start(ap, fmt); vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); va_end(ap); return __libc_android_log_write(prio, tag, buf); }
__LIBC_HIDDEN__ int __libc_android_log_assert(const char *cond, const char *tag, const char *fmt, ...) { va_list ap; char buf[LOG_BUF_SIZE]; va_start(ap, fmt); vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); va_end(ap); __libc_android_log_write(ANDROID_LOG_FATAL, tag, buf); exit(1); return -1; }
/* * Writes a summary of the signal to the log file. We do this so that, if * for some reason we're not able to contact debuggerd, there is still some * indication of the failure in the log. * * We could be here as a result of native heap corruption, or while a * mutex is being held, so we don't want to use any libc functions that * could allocate memory or hold a lock. */ static void logSignalSummary(int signum, const siginfo_t* info) { const char* signame; switch (signum) { case SIGILL: signame = "SIGILL"; break; case SIGABRT: signame = "SIGABRT"; break; case SIGBUS: signame = "SIGBUS"; break; case SIGFPE: signame = "SIGFPE"; break; case SIGSEGV: signame = "SIGSEGV"; break; #if defined(SIGSTKFLT) case SIGSTKFLT: signame = "SIGSTKFLT"; break; #endif case SIGPIPE: signame = "SIGPIPE"; break; default: signame = "???"; break; } char threadname[MAX_TASK_NAME_LEN + 1]; // one more for termination if (prctl(PR_GET_NAME, (unsigned long)threadname, 0, 0, 0) != 0) { strcpy(threadname, "<name unknown>"); } else { // short names are null terminated by prctl, but the manpage // implies that 16 byte names are not. threadname[MAX_TASK_NAME_LEN] = 0; } char buffer[128]; // "info" will be NULL if the siginfo_t information was not available. if (info != NULL) { format_buffer(buffer, sizeof(buffer), "Fatal signal %d (%s) at 0x%08x (code=%d), thread %d (%s)", signum, signame, reinterpret_cast<uintptr_t>(info->si_addr), info->si_code, gettid(), threadname); } else { format_buffer(buffer, sizeof(buffer), "Fatal signal %d (%s), thread %d (%s)", signum, signame, gettid(), threadname); } __libc_android_log_write(ANDROID_LOG_FATAL, "libc", buffer); }
/* * Catches fatal signals so we can ask debuggerd to ptrace us before * we crash. */ void debugger_signal_handler(int n, siginfo_t* info, void*) { char msgbuf[128]; /* * It's possible somebody cleared the SA_SIGINFO flag, which would mean * our "info" arg holds an undefined value. */ if (!haveSiginfo(n)) { info = NULL; } logSignalSummary(n, info); pid_t tid = gettid(); int s = socket_abstract_client(DEBUGGER_SOCKET_NAME, SOCK_STREAM); if (s >= 0) { /* debugger knows our pid from the credentials on the * local socket but we need to tell it our tid. It * is paranoid and will verify that we are giving a tid * that's actually in our process */ int ret; debugger_msg_t msg; msg.action = DEBUGGER_ACTION_CRASH; msg.tid = tid; ret = TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg))); if (ret == sizeof(msg)) { /* if the write failed, there is no point to read on * the file descriptor. */ ret = TEMP_FAILURE_RETRY(read(s, &tid, 1)); int saved_errno = errno; notify_gdb_of_libraries(); errno = saved_errno; } if (ret < 0) { /* read or write failed -- broken connection? */ format_buffer(msgbuf, sizeof(msgbuf), "Failed while talking to debuggerd: %s", strerror(errno)); __libc_android_log_write(ANDROID_LOG_FATAL, "libc", msgbuf); } close(s); } else { /* socket failed; maybe process ran out of fds */ format_buffer(msgbuf, sizeof(msgbuf), "Unable to open connection to debuggerd: %s", strerror(errno)); __libc_android_log_write(ANDROID_LOG_FATAL, "libc", msgbuf); } /* remove our net so we fault for real when we return */ signal(n, SIG_DFL); /* * 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. */ switch (n) { case SIGABRT: case SIGFPE: case SIGPIPE: #ifdef SIGSTKFLT case SIGSTKFLT: #endif (void) tgkill(getpid(), gettid(), n); break; default: // SIGILL, SIGBUS, SIGSEGV break; } }