aboutsummaryrefslogtreecommitdiff
path: root/src/lib/err/backtrace.c
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2018-06-21 09:53:08 -0400
committerNick Mathewson <nickm@torproject.org>2018-06-21 10:47:11 -0400
commit2cfcb7b364541c27749ae0df73eabd7a56797f93 (patch)
tree1fb03761e4631f1d5edd893fc9164a49ff8981b6 /src/lib/err/backtrace.c
parentb2346b12019c45288bbae3bd009fe0bafe80ff58 (diff)
downloadtor-2cfcb7b364541c27749ae0df73eabd7a56797f93.tar.gz
tor-2cfcb7b364541c27749ae0df73eabd7a56797f93.zip
Extract error functionality into a new lowest-level library.
Diffstat (limited to 'src/lib/err/backtrace.c')
-rw-r--r--src/lib/err/backtrace.c282
1 files changed, 282 insertions, 0 deletions
diff --git a/src/lib/err/backtrace.c b/src/lib/err/backtrace.c
new file mode 100644
index 0000000000..d83cbba0e8
--- /dev/null
+++ b/src/lib/err/backtrace.c
@@ -0,0 +1,282 @@
+/* Copyright (c) 2013-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file backtrace.c
+ *
+ * \brief Functions to produce backtraces on bugs, crashes, or assertion
+ * failures.
+ *
+ * Currently, we've only got an implementation here using the backtrace()
+ * family of functions, which are sometimes provided by libc and sometimes
+ * provided by libexecinfo. We tie into the sigaction() backend in order to
+ * detect crashes.
+ */
+
+#include "orconfig.h"
+#include "common/torerr.h"
+
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_CYGWIN_SIGNAL_H
+#include <cygwin/signal.h>
+#elif defined(HAVE_SYS_UCONTEXT_H)
+#include <sys/ucontext.h>
+#elif defined(HAVE_UCONTEXT_H)
+#include <ucontext.h>
+#endif /* defined(HAVE_CYGWIN_SIGNAL_H) || ... */
+
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+#define EXPOSE_CLEAN_BACKTRACE
+#include "common/backtrace.h"
+#include "common/torerr.h"
+
+#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && \
+ defined(HAVE_BACKTRACE_SYMBOLS_FD) && defined(HAVE_SIGACTION)
+#define USE_BACKTRACE
+#endif
+
+#if !defined(USE_BACKTRACE)
+#define NO_BACKTRACE_IMPL
+#endif
+
+// Redundant with util.h, but doing it here so we can avoid that dependency.
+#define raw_free free
+
+#ifdef USE_BACKTRACE
+/** Version of Tor to report in backtrace messages. */
+static char bt_version[128] = "";
+
+/** Largest stack depth to try to dump. */
+#define MAX_DEPTH 256
+/** Static allocation of stack to dump. This is static so we avoid stack
+ * pressure. */
+static void *cb_buf[MAX_DEPTH];
+/** Protects cb_buf from concurrent access. Pthreads, since this code
+ * is Unix-only, and since this code needs to be lowest-level. */
+static pthread_mutex_t cb_buf_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/** Change a stacktrace in <b>stack</b> of depth <b>depth</b> so that it will
+ * log the correct function from which a signal was received with context
+ * <b>ctx</b>. (When we get a signal, the current function will not have
+ * called any other function, and will therefore have not pushed its address
+ * onto the stack. Fortunately, we usually have the program counter in the
+ * ucontext_t structure.
+ */
+void
+clean_backtrace(void **stack, size_t depth, const ucontext_t *ctx)
+{
+#ifdef PC_FROM_UCONTEXT
+#if defined(__linux__)
+ const size_t n = 1;
+#elif defined(__darwin__) || defined(__APPLE__) || defined(OpenBSD) \
+ || defined(__FreeBSD__)
+ const size_t n = 2;
+#else
+ const size_t n = 1;
+#endif /* defined(__linux__) || ... */
+ if (depth <= n)
+ return;
+
+ stack[n] = (void*) ctx->PC_FROM_UCONTEXT;
+#else /* !(defined(PC_FROM_UCONTEXT)) */
+ (void) depth;
+ (void) ctx;
+ (void) stack;
+#endif /* defined(PC_FROM_UCONTEXT) */
+}
+
+/** Log a message <b>msg</b> at <b>severity</b> in <b>domain</b>, and follow
+ * that with a backtrace log. Send messages via the tor_log function at
+ * logger". */
+void
+log_backtrace_impl(int severity, int domain, const char *msg,
+ tor_log_fn logger)
+{
+ size_t depth;
+ char **symbols;
+ size_t i;
+
+ pthread_mutex_lock(&cb_buf_mutex);
+
+ depth = backtrace(cb_buf, MAX_DEPTH);
+ symbols = backtrace_symbols(cb_buf, (int)depth);
+
+ logger(severity, domain, "%s. Stack trace:", msg);
+ if (!symbols) {
+ /* LCOV_EXCL_START -- we can't provoke this. */
+ logger(severity, domain, " Unable to generate backtrace.");
+ goto done;
+ /* LCOV_EXCL_STOP */
+ }
+ for (i=0; i < depth; ++i) {
+ logger(severity, domain, " %s", symbols[i]);
+ }
+ raw_free(symbols);
+
+ done:
+ pthread_mutex_unlock(&cb_buf_mutex);
+}
+
+static void crash_handler(int sig, siginfo_t *si, void *ctx_)
+ __attribute__((noreturn));
+
+/** Signal handler: write a crash message with a stack trace, and die. */
+static void
+crash_handler(int sig, siginfo_t *si, void *ctx_)
+{
+ char buf[40];
+ size_t depth;
+ ucontext_t *ctx = (ucontext_t *) ctx_;
+ int n_fds, i;
+ const int *fds = NULL;
+
+ (void) si;
+
+ depth = backtrace(cb_buf, MAX_DEPTH);
+ /* Clean up the top stack frame so we get the real function
+ * name for the most recently failing function. */
+ clean_backtrace(cb_buf, depth, ctx);
+
+ format_dec_number_sigsafe((unsigned)sig, buf, sizeof(buf));
+
+ tor_log_err_sigsafe(bt_version, " died: Caught signal ", buf, "\n",
+ NULL);
+
+ n_fds = tor_log_get_sigsafe_err_fds(&fds);
+ for (i=0; i < n_fds; ++i)
+ backtrace_symbols_fd(cb_buf, (int)depth, fds[i]);
+
+ abort();
+}
+
+/** Write a backtrace to all of the emergency-error fds. */
+void
+dump_stack_symbols_to_error_fds(void)
+{
+ int n_fds, i;
+ const int *fds = NULL;
+ size_t depth;
+
+ depth = backtrace(cb_buf, MAX_DEPTH);
+
+ n_fds = tor_log_get_sigsafe_err_fds(&fds);
+ for (i=0; i < n_fds; ++i)
+ backtrace_symbols_fd(cb_buf, (int)depth, fds[i]);
+}
+
+/** Install signal handlers as needed so that when we crash, we produce a
+ * useful stack trace. Return 0 on success, -errno on failure. */
+static int
+install_bt_handler(const char *software)
+{
+ int trap_signals[] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGSYS,
+ SIGIO, -1 };
+ int i, rv=0;
+
+ strncpy(bt_version, software, sizeof(bt_version) - 1);
+ bt_version[sizeof(bt_version) - 1] = 0;
+
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = crash_handler;
+ sa.sa_flags = SA_SIGINFO;
+ sigfillset(&sa.sa_mask);
+
+ for (i = 0; trap_signals[i] >= 0; ++i) {
+ if (sigaction(trap_signals[i], &sa, NULL) == -1) {
+ /* LCOV_EXCL_START */
+ rv = -errno;
+ /* LCOV_EXCL_STOP */
+ }
+ }
+
+ {
+ /* Now, generate (but do not log) a backtrace. This ensures that
+ * libc has pre-loaded the symbols we need to dump things, so that later
+ * reads won't be denied by the sandbox code */
+ char **symbols;
+ size_t depth = backtrace(cb_buf, MAX_DEPTH);
+ symbols = backtrace_symbols(cb_buf, (int) depth);
+ if (symbols)
+ raw_free(symbols);
+ }
+
+ return rv;
+}
+
+/** Uninstall crash handlers. */
+static void
+remove_bt_handler(void)
+{
+}
+#endif /* defined(USE_BACKTRACE) */
+
+#ifdef NO_BACKTRACE_IMPL
+void
+log_backtrace_impl(int severity, int domain, const char *msg,
+ tor_log_fn logger)
+{
+ logger(severity, domain, "%s. (Stack trace not available)", msg);
+}
+
+static int
+install_bt_handler(const char *software)
+{
+ (void) software;
+ return 0;
+}
+
+static void
+remove_bt_handler(void)
+{
+}
+
+void
+dump_stack_symbols_to_error_fds(void)
+{
+}
+#endif /* defined(NO_BACKTRACE_IMPL) */
+
+/** Set up code to handle generating error messages on crashes. */
+int
+configure_backtrace_handler(const char *tor_version)
+{
+ char version[128];
+ strncpy(version, "Tor", sizeof(version)-1);
+
+ if (tor_version) {
+ strncat(version, " ", sizeof(version)-1);
+ strncat(version, tor_version, sizeof(version)-1);
+ }
+
+ version[sizeof(version) - 1] = 0;
+
+ return install_bt_handler(version);
+}
+
+/** Perform end-of-process cleanup for code that generates error messages on
+ * crashes. */
+void
+clean_up_backtrace_handler(void)
+{
+ remove_bt_handler();
+}