summaryrefslogtreecommitdiff
path: root/src/lib/err
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
parentb2346b12019c45288bbae3bd009fe0bafe80ff58 (diff)
downloadtor-2cfcb7b364541c27749ae0df73eabd7a56797f93.tar.gz
tor-2cfcb7b364541c27749ae0df73eabd7a56797f93.zip
Extract error functionality into a new lowest-level library.
Diffstat (limited to 'src/lib/err')
-rw-r--r--src/lib/err/backtrace.c282
-rw-r--r--src/lib/err/backtrace.h29
-rw-r--r--src/lib/err/include.am17
-rw-r--r--src/lib/err/torerr.c229
-rw-r--r--src/lib/err/torerr.h47
5 files changed, 604 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();
+}
diff --git a/src/lib/err/backtrace.h b/src/lib/err/backtrace.h
new file mode 100644
index 0000000000..0a145fc92d
--- /dev/null
+++ b/src/lib/err/backtrace.h
@@ -0,0 +1,29 @@
+/* Copyright (c) 2013-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_BACKTRACE_H
+#define TOR_BACKTRACE_H
+
+#include "orconfig.h"
+#include "common/compat_compiler.h"
+
+typedef void (*tor_log_fn)(int, unsigned, const char *fmt, ...)
+ CHECK_PRINTF(3,4);
+
+void log_backtrace_impl(int severity, int domain, const char *msg,
+ tor_log_fn logger);
+int configure_backtrace_handler(const char *tor_version);
+void clean_up_backtrace_handler(void);
+void dump_stack_symbols_to_error_fds(void);
+
+#define log_backtrace(sev, dom, msg) \
+ log_backtrace_impl((sev), (dom), (msg), tor_log)
+
+#ifdef EXPOSE_CLEAN_BACKTRACE
+#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && \
+ defined(HAVE_BACKTRACE_SYMBOLS_FD) && defined(HAVE_SIGACTION)
+void clean_backtrace(void **stack, size_t depth, const ucontext_t *ctx);
+#endif
+#endif /* defined(EXPOSE_CLEAN_BACKTRACE) */
+
+#endif /* !defined(TOR_BACKTRACE_H) */
diff --git a/src/lib/err/include.am b/src/lib/err/include.am
new file mode 100644
index 0000000000..257ba3004b
--- /dev/null
+++ b/src/lib/err/include.am
@@ -0,0 +1,17 @@
+
+noinst_LIBRARIES += src/lib/libtor-err.a
+
+if UNITTESTS_ENABLED
+noinst_LIBRARIES += src/lib/libtor-err-testing.a
+endif
+
+src_lib_libtor_err_a_SOURCES = \
+ src/lib/err/backtrace.c \
+ src/lib/err/torerr.c
+
+src_lib_libtor_err_testing_a_SOURCES = \
+ $(src_lib_libtor_err_a_SOURCES)
+
+noinst_HEADERS += \
+ src/lib/err/backtrace.h \
+ src/lib/err/torerr.h
diff --git a/src/lib/err/torerr.c b/src/lib/err/torerr.c
new file mode 100644
index 0000000000..0e0c4db355
--- /dev/null
+++ b/src/lib/err/torerr.c
@@ -0,0 +1,229 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file torerr.c
+ *
+ * \brief Handling code for unrecoverable emergencies, at a lower level
+ * than the logging code.
+ */
+
+#include "orconfig.h"
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "common/torerr.h"
+#include "common/backtrace.h"
+
+/** Array of fds to log crash-style warnings to. */
+static int sigsafe_log_fds[TOR_SIGSAFE_LOG_MAX_FDS] = { STDERR_FILENO };
+/** The number of elements used in sigsafe_log_fds */
+static int n_sigsafe_log_fds = 1;
+/** Log granularity in milliseconds. */
+static int log_granularity = 1000;
+
+/** Write <b>s</b> to each element of sigsafe_log_fds. Return 0 on success, -1
+ * on failure. */
+static int
+tor_log_err_sigsafe_write(const char *s)
+{
+ int i;
+ ssize_t r;
+ size_t len = strlen(s);
+ int err = 0;
+ for (i=0; i < n_sigsafe_log_fds; ++i) {
+ r = write(sigsafe_log_fds[i], s, len);
+ err += (r != (ssize_t)len);
+ }
+ return err ? -1 : 0;
+}
+
+/** Given a list of string arguments ending with a NULL, writes them
+ * to our logs and to stderr (if possible). This function is safe to call
+ * from within a signal handler. */
+void
+tor_log_err_sigsafe(const char *m, ...)
+{
+ va_list ap;
+ const char *x;
+ char timebuf[33];
+ time_t now = time(NULL);
+
+ if (!m)
+ return;
+ if (log_granularity >= 2000) {
+ int g = log_granularity / 1000;
+ now -= now % g;
+ }
+ timebuf[0] = now < 0 ? '-' : ' ';
+ if (now < 0) now = -now;
+ timebuf[1] = '\0';
+ format_dec_number_sigsafe(now, timebuf+1, sizeof(timebuf)-1);
+ tor_log_err_sigsafe_write("\n=========================================="
+ "================== T=");
+ tor_log_err_sigsafe_write(timebuf);
+ tor_log_err_sigsafe_write("\n");
+ tor_log_err_sigsafe_write(m);
+ va_start(ap, m);
+ while ((x = va_arg(ap, const char*))) {
+ tor_log_err_sigsafe_write(x);
+ }
+ va_end(ap);
+}
+
+/** Set *<b>out</b> to a pointer to an array of the fds to log errors to from
+ * inside a signal handler or other emergency condition. Return the number of
+ * elements in the array. */
+int
+tor_log_get_sigsafe_err_fds(const int **out)
+{
+ *out = sigsafe_log_fds;
+ return n_sigsafe_log_fds;
+}
+
+/**
+ * Update the list of fds that get errors from inside a signal handler or
+ * other emergency condition. Ignore any beyond the first
+ * TOR_SIGSAFE_LOG_MAX_FDS.
+ */
+void
+tor_log_set_sigsafe_err_fds(const int *fds, int n)
+{
+ if (n > TOR_SIGSAFE_LOG_MAX_FDS) {
+ n = TOR_SIGSAFE_LOG_MAX_FDS;
+ }
+
+ memcpy(sigsafe_log_fds, fds, n * sizeof(int));
+ n_sigsafe_log_fds = n;
+}
+
+/**
+ * Set the granularity (in ms) to use when reporting fatal errors outside
+ * the logging system.
+ */
+void
+tor_log_sigsafe_err_set_granularity(int ms)
+{
+ log_granularity = ms;
+}
+
+/**
+ * Log an emergency assertion failure message.
+ *
+ * This kind of message is safe to send from within a log handler,
+ * a signal handler, or other emergency situation.
+ */
+void
+tor_raw_assertion_failed_msg_(const char *file, int line, const char *expr,
+ const char *msg)
+{
+ char linebuf[16];
+ format_dec_number_sigsafe(line, linebuf, sizeof(linebuf));
+ tor_log_err_sigsafe("INTERNAL ERROR: Raw assertion failed at ",
+ file, ":", linebuf, ": ", expr, NULL);
+ if (msg) {
+ tor_log_err_sigsafe_write(msg);
+ tor_log_err_sigsafe_write("\n");
+ }
+
+ dump_stack_symbols_to_error_fds();
+}
+
+/* As format_{hex,dex}_number_sigsafe, but takes a <b>radix</b> argument
+ * in range 2..16 inclusive. */
+static int
+format_number_sigsafe(unsigned long x, char *buf, int buf_len,
+ unsigned int radix)
+{
+ unsigned long tmp;
+ int len;
+ char *cp;
+
+ /* NOT tor_assert. This needs to be safe to run from within a signal
+ * handler, and from within the 'tor_assert() has failed' code. Not even
+ * raw_assert(), since raw_assert() calls this function on failure. */
+ if (radix < 2 || radix > 16)
+ return 0;
+
+ /* Count how many digits we need. */
+ tmp = x;
+ len = 1;
+ while (tmp >= radix) {
+ tmp /= radix;
+ ++len;
+ }
+
+ /* Not long enough */
+ if (!buf || len >= buf_len)
+ return 0;
+
+ cp = buf + len;
+ *cp = '\0';
+ do {
+ unsigned digit = (unsigned) (x % radix);
+ if (cp <= buf) {
+ /* Not tor_assert(); see above. */
+ abort();
+ }
+ --cp;
+ *cp = "0123456789ABCDEF"[digit];
+ x /= radix;
+ } while (x);
+
+ /* NOT tor_assert; see above. */
+ if (cp != buf) {
+ abort(); // LCOV_EXCL_LINE
+ }
+
+ return len;
+}
+
+/**
+ * Helper function to output hex numbers from within a signal handler.
+ *
+ * Writes the nul-terminated hexadecimal digits of <b>x</b> into a buffer
+ * <b>buf</b> of size <b>buf_len</b>, and return the actual number of digits
+ * written, not counting the terminal NUL.
+ *
+ * If there is insufficient space, write nothing and return 0.
+ *
+ * This accepts an unsigned int because format_helper_exit_status() needs to
+ * call it with a signed int and an unsigned char, and since the C standard
+ * does not guarantee that an int is wider than a char (an int must be at
+ * least 16 bits but it is permitted for a char to be that wide as well), we
+ * can't assume a signed int is sufficient to accommodate an unsigned char.
+ * Thus, format_helper_exit_status() will still need to emit any require '-'
+ * on its own.
+ *
+ * For most purposes, you'd want to use tor_snprintf("%x") instead of this
+ * function; it's designed to be used in code paths where you can't call
+ * arbitrary C functions.
+ */
+int
+format_hex_number_sigsafe(unsigned long x, char *buf, int buf_len)
+{
+ return format_number_sigsafe(x, buf, buf_len, 16);
+}
+
+/** As format_hex_number_sigsafe, but format the number in base 10. */
+int
+format_dec_number_sigsafe(unsigned long x, char *buf, int buf_len)
+{
+ return format_number_sigsafe(x, buf, buf_len, 10);
+}
diff --git a/src/lib/err/torerr.h b/src/lib/err/torerr.h
new file mode 100644
index 0000000000..10d9f481c0
--- /dev/null
+++ b/src/lib/err/torerr.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file torerr.h
+ *
+ * \brief Headers for torerr.c.
+ **/
+
+#ifndef TOR_TORERR_H
+#define TOR_TORERR_H
+
+#include "common/compat_compiler.h"
+
+/* The raw_assert...() variants are for use within code that can't call
+ * tor_assertion_failed_() because of call circularity issues. */
+#define raw_assert(expr) STMT_BEGIN \
+ if (!(expr)) { \
+ tor_raw_assertion_failed_msg_(__FILE__, __LINE__, #expr, NULL); \
+ abort(); \
+ } \
+ STMT_END
+#define raw_assert_unreached(expr) raw_assert(0)
+#define raw_assert_unreached_msg(msg) STMT_BEGIN \
+ tor_raw_assertion_failed_msg_(__FILE__, __LINE__, "0", (msg)); \
+ abort(); \
+ STMT_END
+
+void tor_raw_assertion_failed_msg_(const char *file, int line,
+ const char *expr,
+ const char *msg);
+
+/** Maximum number of fds that will get notifications if we crash */
+#define TOR_SIGSAFE_LOG_MAX_FDS 8
+
+void tor_log_err_sigsafe(const char *m, ...);
+int tor_log_get_sigsafe_err_fds(const int **out);
+void tor_log_set_sigsafe_err_fds(const int *fds, int n);
+void tor_log_sigsafe_err_set_granularity(int ms);
+
+int format_hex_number_sigsafe(unsigned long x, char *buf, int max_len);
+int format_dec_number_sigsafe(unsigned long x, char *buf, int max_len);
+
+#endif /* !defined(TOR_TORLOG_H) */