aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/backtrace.c202
-rw-r--r--src/common/backtrace.h12
-rw-r--r--src/common/include.am2
-rw-r--r--src/common/log.c140
-rw-r--r--src/common/sandbox.c47
-rw-r--r--src/common/sandbox.h3
-rw-r--r--src/common/torlog.h8
-rw-r--r--src/common/util.c111
-rw-r--r--src/common/util.h17
-rw-r--r--src/or/config.c3
-rw-r--r--src/or/main.c10
-rwxr-xr-xsrc/test/bt_test.py42
-rw-r--r--src/test/include.am12
-rw-r--r--src/test/test.c3
-rw-r--r--src/test/test_bt_cl.c110
-rw-r--r--src/test/test_logging.c135
-rw-r--r--src/test/test_util.c51
17 files changed, 794 insertions, 114 deletions
diff --git a/src/common/backtrace.c b/src/common/backtrace.c
new file mode 100644
index 000000000..78bc66efb
--- /dev/null
+++ b/src/common/backtrace.c
@@ -0,0 +1,202 @@
+/* Copyright (c) 2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "backtrace.h"
+#include "compat.h"
+#include "util.h"
+#include "torlog.h"
+
+#define __USE_GNU
+
+#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
+#ifdef HAVE_UCONTEXT_H
+#include <ucontext.h>
+#endif
+#ifdef HAVE_SYS_UCONTEXT_H
+#include <sys/ucontext.h>
+#endif
+
+#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
+
+/** Version of Tor to report in backtrace messages. */
+static char *bt_version = NULL;
+
+#ifdef USE_BACKTRACE
+/** 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];
+
+/** 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.
+ */
+static void
+clean_backtrace(void **stack, int depth, const ucontext_t *ctx)
+{
+#ifdef PC_FROM_UCONTEXT
+#if defined(__linux__)
+ const int n = 1;
+#elif defined(__darwin__) || defined(__APPLE__) || defined(__OpenBSD__) \
+ || defined(__FreeBSD__)
+ const int n = 2;
+#else
+ const int n = 1;
+#endif
+ if (depth <= n)
+ return;
+
+ stack[n] = (void*) ctx->PC_FROM_UCONTEXT;
+#else
+ (void) depth;
+ (void) ctx;
+#endif
+}
+
+/** Log a message <b>msg</b> at <b>severity</b> in <b>domain</b>, and follow
+ * that with a backtrace log. */
+void
+log_backtrace(int severity, int domain, const char *msg)
+{
+ int depth = backtrace(cb_buf, MAX_DEPTH);
+ char **symbols = backtrace_symbols(cb_buf, depth);
+ int i;
+ tor_log(severity, domain, "%s. Stack trace:", msg);
+ if (!symbols) {
+ tor_log(severity, domain, " Unable to generate backtrace.");
+ return;
+ }
+ for (i=0; i < depth; ++i) {
+ tor_log(severity, domain, " %s", symbols[i]);
+ }
+ free(symbols);
+}
+
+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];
+ int 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, depth, fds[i]);
+
+ abort();
+}
+
+/** Install signal handlers as needed so that when we crash, we produce a
+ * useful stack trace. Return 0 on success, -1 on failure. */
+static int
+install_bt_handler(void)
+{
+ int trap_signals[] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGSYS,
+ SIGIO, -1 };
+ int i, rv=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) {
+ log_warn(LD_BUG, "Sigaction failed: %s", strerror(errno));
+ rv = -1;
+ }
+ }
+ return rv;
+}
+
+/** Uninstall crash handlers. */
+static void
+remove_bt_handler(void)
+{
+ /* We don't need to actually free anything at exit here. */
+}
+#endif
+
+#ifdef NO_BACKTRACE_IMPL
+void
+log_backtrace(int severity, int domain, const char *msg)
+{
+ tor_log(severity, domain, "%s. (Stack trace not available)", msg);
+}
+
+static int
+install_bt_handler(void)
+{
+ return 0;
+}
+
+static void
+remove_bt_handler(void)
+{
+}
+#endif
+
+/** Set up code to handle generating error messages on crashes. */
+int
+configure_backtrace_handler(const char *tor_version)
+{
+ tor_free(bt_version);
+ if (!tor_version)
+ tor_version = "";
+ tor_asprintf(&bt_version, "Tor %s", tor_version);
+
+ return install_bt_handler();
+}
+
+/** Perform end-of-process cleanup for code that generates error messages on
+ * crashes. */
+void
+clean_up_backtrace_handler(void)
+{
+ remove_bt_handler();
+
+ tor_free(bt_version);
+}
+
diff --git a/src/common/backtrace.h b/src/common/backtrace.h
new file mode 100644
index 000000000..765436fee
--- /dev/null
+++ b/src/common/backtrace.h
@@ -0,0 +1,12 @@
+/* Copyright (c) 2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_BACKTRACE_H
+#define TOR_BACKTRACE_H
+
+void log_backtrace(int severity, int domain, const char *msg);
+int configure_backtrace_handler(const char *tor_version);
+void clean_up_backtrace_handler(void);
+
+#endif
+
diff --git a/src/common/include.am b/src/common/include.am
index 032befd20..814786b77 100644
--- a/src/common/include.am
+++ b/src/common/include.am
@@ -50,6 +50,7 @@ endif
LIBOR_A_SOURCES = \
src/common/address.c \
+ src/common/backtrace.c \
src/common/compat.c \
src/common/container.c \
src/common/di_ops.c \
@@ -90,6 +91,7 @@ src_common_libor_event_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
COMMONHEADERS = \
src/common/address.h \
+ src/common/backtrace.h \
src/common/aes.h \
src/common/ciphers.inc \
src/common/compat.h \
diff --git a/src/common/log.c b/src/common/log.c
index 303fba93a..dffda45c5 100644
--- a/src/common/log.c
+++ b/src/common/log.c
@@ -87,12 +87,12 @@ should_log_function_name(log_domain_mask_t domain, int severity)
case LOG_DEBUG:
case LOG_INFO:
/* All debugging messages occur in interesting places. */
- return 1;
+ return (domain & LD_NOFUNCNAME) == 0;
case LOG_NOTICE:
case LOG_WARN:
case LOG_ERR:
/* We care about places where bugs occur. */
- return (domain == LD_BUG);
+ return (domain & (LD_BUG|LD_NOFUNCNAME)) == LD_BUG;
default:
/* Call assert, not tor_assert, since tor_assert calls log on failure. */
assert(0); return 0;
@@ -443,6 +443,126 @@ tor_log(int severity, log_domain_mask_t domain, const char *format, ...)
va_end(ap);
}
+/** Maximum number of fds that will get notifications if we crash */
+#define MAX_SIGSAFE_FDS 8
+/** Array of fds to log crash-style warnings to. */
+static int sigsafe_log_fds[MAX_SIGSAFE_FDS] = { STDERR_FILENO };
+/** The number of elements used in sigsafe_log_fds */
+static int n_sigsafe_log_fds = 1;
+
+/** 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[32];
+ time_t now = time(NULL);
+
+ if (!m)
+ return;
+ if (log_time_granularity >= 2000) {
+ int g = log_time_granularity / 1000;
+ now -= now % g;
+ }
+ timebuf[0] = '\0';
+ format_dec_number_sigsafe(now, timebuf, sizeof(timebuf));
+ 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. 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;
+}
+
+/** Helper function; return true iff the <b>n</b>-element array <b>array</b>
+ * contains <b>item</b>. */
+static int
+int_array_contains(const int *array, int n, int item)
+{
+ int j;
+ for (j = 0; j < n; ++j) {
+ if (array[j] == item)
+ return 1;
+ }
+ return 0;
+}
+
+/** Function to call whenever the list of logs changes to get ready to log
+ * from signal handlers. */
+void
+tor_log_update_sigsafe_err_fds(void)
+{
+ const logfile_t *lf;
+ int found_real_stderr = 0;
+
+ LOCK_LOGS();
+ /* Reserve the first one for stderr. This is safe because when we daemonize,
+ * we dup2 /dev/null to stderr, */
+ sigsafe_log_fds[0] = STDERR_FILENO;
+ n_sigsafe_log_fds = 1;
+
+ for (lf = logfiles; lf; lf = lf->next) {
+ /* Don't try callback to the control port, or syslogs: We can't
+ * do them from a signal handler. Don't try stdout: we always do stderr.
+ */
+ if (lf->is_temporary || lf->is_syslog ||
+ lf->callback || lf->seems_dead || lf->fd < 0)
+ continue;
+ if (lf->severities->masks[SEVERITY_MASK_IDX(LOG_ERR)] &
+ (LD_BUG|LD_GENERAL)) {
+ if (lf->fd == STDERR_FILENO)
+ found_real_stderr = 1;
+ /* Avoid duplicates */
+ if (int_array_contains(sigsafe_log_fds, n_sigsafe_log_fds, lf->fd))
+ continue;
+ sigsafe_log_fds[n_sigsafe_log_fds++] = lf->fd;
+ if (n_sigsafe_log_fds == MAX_SIGSAFE_FDS)
+ break;
+ }
+ }
+
+ if (!found_real_stderr &&
+ int_array_contains(sigsafe_log_fds, n_sigsafe_log_fds, STDOUT_FILENO)) {
+ /* Don't use a virtual stderr when we're also logging to stdout. */
+ assert(n_sigsafe_log_fds >= 2); /* Don't use assert inside log functions*/
+ sigsafe_log_fds[0] = sigsafe_log_fds[--n_sigsafe_log_fds];
+ }
+
+ UNLOCK_LOGS();
+}
+
/** Output a message to the log, prefixed with a function name <b>fn</b>. */
#ifdef __GNUC__
/** GCC-based implementation of the log_fn backend, used when we have
@@ -1142,22 +1262,6 @@ get_min_log_level(void)
return min;
}
-/** Return the fd of a file log that is receiving ERR messages, or -1 if
- * no such log exists. */
-int
-get_err_logging_fd(void)
-{
- const logfile_t *lf;
- for (lf = logfiles; lf; lf = lf->next) {
- if (lf->is_temporary || lf->is_syslog || !lf->filename ||
- lf->callback || lf->seems_dead || lf->fd < 0)
- continue;
- if (lf->severities->masks[LOG_ERR] & LD_GENERAL)
- return lf->fd;
- }
- return -1;
-}
-
/** Switch all logs to output at most verbose level. */
void
switch_logs_debug(void)
diff --git a/src/common/sandbox.c b/src/common/sandbox.c
index aaba9c776..7ef577dbe 100644
--- a/src/common/sandbox.c
+++ b/src/common/sandbox.c
@@ -1290,16 +1290,6 @@ install_syscall_filter(sandbox_cfg_t* cfg)
return (rc < 0 ? -rc : rc);
}
-/** Additional file descriptor to use when logging seccomp2 failures */
-static int sigsys_debugging_fd = -1;
-
-/** Use the file descriptor <b>fd</b> to log seccomp2 failures. */
-static void
-sigsys_set_debugging_fd(int fd)
-{
- sigsys_debugging_fd = fd;
-}
-
/**
* Function called when a SIGSYS is caught by the application. It notifies the
* user that an error has occurred and either terminates or allows the
@@ -1309,8 +1299,8 @@ static void
sigsys_debugging(int nr, siginfo_t *info, void *void_context)
{
ucontext_t *ctx = (ucontext_t *) (void_context);
- char message[256];
- int rv = 0, syscall, length, err;
+ char number[32];
+ int syscall;
(void) nr;
if (info->si_code != SYS_SECCOMP)
@@ -1321,24 +1311,11 @@ sigsys_debugging(int nr, siginfo_t *info, void *void_context)
syscall = ctx->uc_mcontext.gregs[REG_SYSCALL];
- strlcpy(message, "\n\n(Sandbox) Caught a bad syscall attempt (syscall 0x",
- sizeof(message));
- (void) format_hex_number_sigsafe(syscall, message+strlen(message),
- sizeof(message)-strlen(message));
- strlcat(message, ")\n", sizeof(message));
- length = strlen(message);
-
- err = 0;
- if (sigsys_debugging_fd >= 0) {
- rv = write(sigsys_debugging_fd, message, length);
- err += rv != length;
- }
-
- rv = write(STDOUT_FILENO, message, length);
- err += rv != length;
-
- if (err)
- _exit(2);
+ format_dec_number_sigsafe(syscall, number, sizeof(number));
+ tor_log_err_sigsafe("(Sandbox) Caught a bad syscall attempt (syscall ",
+ number,
+ ")\n",
+ NULL);
#if defined(DEBUGGING_CLOSE)
_exit(1);
@@ -1453,16 +1430,6 @@ sandbox_init(sandbox_cfg_t *cfg)
#endif
}
-void
-sandbox_set_debugging_fd(int fd)
-{
-#ifdef USE_LIBSECCOMP
- sigsys_set_debugging_fd(fd);
-#else
- (void)fd;
-#endif
-}
-
#ifndef USE_LIBSECCOMP
int
sandbox_cfg_allow_open_filename(sandbox_cfg_t **cfg, char *file,
diff --git a/src/common/sandbox.h b/src/common/sandbox.h
index 1d39be409..d64d427d3 100644
--- a/src/common/sandbox.h
+++ b/src/common/sandbox.h
@@ -153,9 +153,6 @@ int sandbox_getaddrinfo(const char *name, const char *servname,
((void)(name))
#endif
-/** Use <b>fd</b> to log non-survivable sandbox violations. */
-void sandbox_set_debugging_fd(int fd);
-
#ifdef USE_LIBSECCOMP
/** Returns a registered protected string used with the sandbox, given that
* it matches the parameter.
diff --git a/src/common/torlog.h b/src/common/torlog.h
index ecd7e121e..d210c8b24 100644
--- a/src/common/torlog.h
+++ b/src/common/torlog.h
@@ -102,6 +102,9 @@
/** This log message is not safe to send to a callback-based logger
* immediately. Used as a flag, not a log domain. */
#define LD_NOCB (1u<<31)
+/** This log message should not include a function name, even if it otherwise
+ * would. Used as a flag, not a log domain. */
+#define LD_NOFUNCNAME (1u<<30)
/** Mask of zero or more log domains, OR'd together. */
typedef uint32_t log_domain_mask_t;
@@ -136,7 +139,6 @@ int get_min_log_level(void);
void switch_logs_debug(void);
void logs_free_all(void);
void add_temp_log(int min_severity);
-int get_err_logging_fd(void);
void close_temp_logs(void);
void rollback_log_changes(void);
void mark_logs_temp(void);
@@ -149,6 +151,10 @@ void set_log_time_granularity(int granularity_msec);
void tor_log(int severity, log_domain_mask_t domain, const char *format, ...)
CHECK_PRINTF(3,4);
+void tor_log_err_sigsafe(const char *m, ...);
+int tor_log_get_sigsafe_err_fds(const int **out);
+void tor_log_update_sigsafe_err_fds(void);
+
#if defined(__GNUC__) || defined(RUNNING_DOXYGEN)
extern int log_global_min_severity_;
diff --git a/src/common/util.c b/src/common/util.c
index 0771c9424..17d6c22ab 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -24,7 +24,8 @@
#include "torint.h"
#include "container.h"
#include "address.h"
-#include "../common/sandbox.h"
+#include "sandbox.h"
+#include "backtrace.h"
#ifdef _WIN32
#include <io.h>
@@ -95,6 +96,23 @@
#endif
/* =====
+ * Assertion helper.
+ * ===== */
+/** Helper for tor_assert: report the assertion failure. */
+void
+tor_assertion_failed_(const char *fname, unsigned int line,
+ const char *func, const char *expr)
+{
+ char buf[256];
+ log_err(LD_BUG, "%s:%u: %s: Assertion %s failed; aborting.",
+ fname, line, func, expr);
+ tor_snprintf(buf, sizeof(buf),
+ "Assertion %s failed in %s at %s:%u",
+ expr, func, fname, line);
+ log_backtrace(LOG_ERR, LD_BUG, buf);
+}
+
+/* =====
* Memory management
* ===== */
#ifdef USE_DMALLOC
@@ -3400,6 +3418,51 @@ tor_join_win_cmdline(const char *argv[])
return joined_argv;
}
+/* 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. */
+ 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 = x % radix;
+ tor_assert(cp > buf);
+ --cp;
+ *cp = "0123456789ABCDEF"[digit];
+ x /= radix;
+ } while (x);
+
+ /* NOT tor_assert; see above. */
+ if (cp != buf) {
+ abort();
+ }
+
+ return len;
+}
+
/**
* Helper function to output hex numbers from within a signal handler.
*
@@ -3422,45 +3485,16 @@ tor_join_win_cmdline(const char *argv[])
* arbitrary C functions.
*/
int
-format_hex_number_sigsafe(unsigned int x, char *buf, int buf_len)
+format_hex_number_sigsafe(unsigned long x, char *buf, int buf_len)
{
- int len;
- unsigned int tmp;
- char *cur;
-
- /* Sanity check */
- if (!buf || buf_len <= 1)
- return 0;
-
- /* How many chars do we need for x? */
- if (x > 0) {
- len = 0;
- tmp = x;
- while (tmp > 0) {
- tmp >>= 4;
- ++len;
- }
- } else {
- len = 1;
- }
-
- /* Bail if we would go past the end of the buffer */
- if (len+1 > buf_len)
- return 0;
-
- /* Point to last one */
- cur = buf + len - 1;
-
- /* Convert x to hex */
- do {
- *cur-- = "0123456789ABCDEF"[x & 0xf];
- x >>= 4;
- } while (x != 0 && cur >= buf);
-
- buf[len] = '\0';
+ return format_number_sigsafe(x, buf, buf_len, 16);
+}
- /* Return len */
- return len;
+/** 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);
}
#ifndef _WIN32
@@ -5047,4 +5081,3 @@ tor_weak_random_range(tor_weak_rng_t *rng, int32_t top)
} while (result >= top);
return result;
}
-
diff --git a/src/common/util.h b/src/common/util.h
index 3199ab112..18dc20639 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -48,13 +48,13 @@
/** Like assert(3), but send assertion failures to the log as well as to
* stderr. */
#define tor_assert(expr) STMT_BEGIN \
- if (PREDICT_UNLIKELY(!(expr))) { \
- log_err(LD_BUG, "%s:%d: %s: Assertion %s failed; aborting.", \
- SHORT_FILE__, __LINE__, __func__, #expr); \
- fprintf(stderr,"%s:%d %s: Assertion %s failed; aborting.\n", \
- SHORT_FILE__, __LINE__, __func__, #expr); \
- abort(); \
- } STMT_END
+ if (PREDICT_UNLIKELY(!(expr))) { \
+ tor_assertion_failed_(SHORT_FILE__, __LINE__, __func__, #expr); \
+ abort(); \
+ } STMT_END
+
+void tor_assertion_failed_(const char *fname, unsigned int line,
+ const char *func, const char *expr);
/* If we're building with dmalloc, we want all of our memory allocation
* functions to take an extra file/line pair of arguments. If not, not.
@@ -524,7 +524,8 @@ int32_t tor_weak_random_range(tor_weak_rng_t *rng, int32_t top);
* <b>n</b> */
#define tor_weak_random_one_in_n(rng, n) (0==tor_weak_random_range((rng),(n)))
-int format_hex_number_sigsafe(unsigned int x, char *buf, int max_len);
+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);
#ifdef UTIL_PRIVATE
/* Prototypes for private functions only used by util.c (and unit tests) */
diff --git a/src/or/config.c b/src/or/config.c
index 87ad7d595..d348f1036 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -1161,8 +1161,6 @@ options_act_reversible(const or_options_t *old_options, char **msg)
goto rollback;
}
- sandbox_set_debugging_fd(get_err_logging_fd());
-
commit:
r = 0;
if (logs_marked) {
@@ -1172,6 +1170,7 @@ options_act_reversible(const or_options_t *old_options, char **msg)
add_callback_log(severity, control_event_logmsg);
control_adjust_event_log_severity();
tor_free(severity);
+ tor_log_update_sigsafe_err_fds();
}
if (get_min_log_level() >= LOG_INFO &&
get_min_log_level() != old_min_log_level) {
diff --git a/src/or/main.c b/src/or/main.c
index aa5642509..e14001f10 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -13,6 +13,7 @@
#define MAIN_PRIVATE
#include "or.h"
#include "addressmap.h"
+#include "backtrace.h"
#include "buffers.h"
#include "channel.h"
#include "channeltls.h"
@@ -2330,13 +2331,14 @@ handle_signals(int is_parent)
int
tor_init(int argc, char *argv[])
{
- char buf[256];
+ char progname[256];
int quiet = 0;
+
time_of_process_start = time(NULL);
init_connection_lists();
/* Have the log set up with our application name. */
- tor_snprintf(buf, sizeof(buf), "Tor %s", get_version());
- log_set_application_name(buf);
+ tor_snprintf(progname, sizeof(progname), "Tor %s", get_version());
+ log_set_application_name(progname);
/* Initialize the history structures. */
rep_hist_init();
/* Initialize the service cache. */
@@ -2839,6 +2841,8 @@ tor_main(int argc, char *argv[])
}
#endif
+ configure_backtrace_handler(get_version());
+
update_approx_time(time(NULL));
tor_threads_init();
init_logging();
diff --git a/src/test/bt_test.py b/src/test/bt_test.py
new file mode 100755
index 000000000..2de9924a5
--- /dev/null
+++ b/src/test/bt_test.py
@@ -0,0 +1,42 @@
+# Copyright 2013, The Tor Project, Inc
+# See LICENSE for licensing information
+
+"""
+bt_test.py
+
+This file tests the output from test-bt-cl to make sure it's as expected.
+
+Example usage:
+
+$ ./src/test/test-bt-cl crash | ./src/test/bt_test.py
+OK
+$ ./src/test/test-bt-cl assert | ./src/test/bt_test.py
+OK
+
+"""
+
+import sys
+
+
+def matches(lines, funcs):
+ if len(lines) < len(funcs):
+ return False
+ try:
+ for l, f in zip(lines, funcs):
+ l.index(f)
+ except ValueError:
+ return False
+ else:
+ return True
+
+FUNCNAMES = "crash oh_what a_tangled_web we_weave main".split()
+
+LINES = sys.stdin.readlines()
+
+for I in range(len(LINES)):
+ if matches(LINES[I:], FUNCNAMES):
+ print "OK"
+ break
+else:
+ print "BAD"
+
diff --git a/src/test/include.am b/src/test/include.am
index 5510293cd..c16dd14fe 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -30,6 +30,7 @@ src_test_test_SOURCES = \
src/test/test_dir.c \
src/test/test_extorport.c \
src/test/test_introduce.c \
+ src/test/test_logging.c \
src/test/test_microdesc.c \
src/test/test_options.c \
src/test/test_pt.c \
@@ -86,6 +87,15 @@ else
CMDLINE_TEST_TOR = ./src/or/tor
endif
+noinst_PROGRAMS += src/test/test-bt-cl
+src_test_test_bt_cl_SOURCES = src/test/test_bt_cl.c
+src_test_test_bt_cl_LDADD = src/common/libor-testing.a \
+ @TOR_LIB_MATH@ \
+ @TOR_LIB_WS32@ @TOR_LIB_GDI@
+src_test_test_bt_cl_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+src_test_test_bt_cl_CPPFLAGS= $(src_test_AM_CPPFLAGS)
+
+
check-local: $(NTOR_TEST_DEPS) $(CMDLINE_TEST_TOR)
if USEPYTHON
$(PYTHON) $(top_srcdir)/src/test/test_cmdline_args.py $(CMDLINE_TEST_TOR) "${top_srcdir}"
@@ -93,4 +103,6 @@ if CURVE25519_ENABLED
$(PYTHON) $(top_srcdir)/src/test/ntor_ref.py test-tor
$(PYTHON) $(top_srcdir)/src/test/ntor_ref.py self-test
endif
+ ./src/test/test-bt-cl assert | $(PYTHON) $(top_srcdir)/src/test/bt_test.py
+ ./src/test/test-bt-cl crash | $(PYTHON) $(top_srcdir)/src/test/bt_test.py
endif
diff --git a/src/test/test.c b/src/test/test.c
index 562de4801..752f8d712 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -1621,6 +1621,8 @@ extern struct testcase_t options_tests[];
extern struct testcase_t socks_tests[];
extern struct testcase_t extorport_tests[];
extern struct testcase_t controller_event_tests[];
+extern struct testcase_t logging_tests[];
+extern struct testcase_t backtrace_tests[];
static struct testgroup_t testgroups[] = {
{ "", test_array },
@@ -1630,6 +1632,7 @@ static struct testgroup_t testgroups[] = {
{ "crypto/", crypto_tests },
{ "container/", container_tests },
{ "util/", util_tests },
+ { "util/logging/", logging_tests },
{ "cellfmt/", cell_format_tests },
{ "cellqueue/", cell_queue_tests },
{ "dir/", dir_tests },
diff --git a/src/test/test_bt_cl.c b/src/test/test_bt_cl.c
new file mode 100644
index 000000000..9ac9823dc
--- /dev/null
+++ b/src/test/test_bt_cl.c
@@ -0,0 +1,110 @@
+/* Copyright (c) 2012-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "or.h"
+#include "util.h"
+#include "backtrace.h"
+#include "torlog.h"
+
+
+/* -1: no crash.
+ * 0: crash with a segmentation fault.
+ * 1x: crash with an assertion failure. */
+static int crashtype = 0;
+
+#ifdef __GNUC__
+#define NOINLINE __attribute__((noinline))
+#define NORETURN __attribute__((noreturn))
+#endif
+
+int crash(int x) NOINLINE;
+int oh_what(int x) NOINLINE;
+int a_tangled_web(int x) NOINLINE;
+int we_weave(int x) NOINLINE;
+static void abort_handler(int s) NORETURN;
+
+int
+crash(int x)
+{
+ if (crashtype == 0) {
+ *(volatile int *)0 = 0;
+ } else if (crashtype == 1) {
+ tor_assert(1 == 0);
+ } else if (crashtype == -1) {
+ ;
+ }
+
+ crashtype *= x;
+ return crashtype;
+}
+
+int
+oh_what(int x)
+{
+ /* We call crash() twice here, so that the compiler won't try to do a
+ * tail-call optimization. Only the first call will actually happen, but
+ * telling the compiler to maybe do the second call will prevent it from
+ * replacing the first call with a jump. */
+ return crash(x) + crash(x*2);
+}
+
+int
+a_tangled_web(int x)
+{
+ return oh_what(x) * 99 + oh_what(x);
+}
+
+int
+we_weave(int x)
+{
+ return a_tangled_web(x) + a_tangled_web(x+1);
+}
+
+static void
+abort_handler(int s)
+{
+ (void)s;
+ exit(0);
+}
+
+int
+main(int argc, char **argv)
+{
+ log_severity_list_t severity;
+
+ if (argc < 2) {
+ puts("I take an argument. It should be \"assert\" or \"crash\" or "
+ "\"none\"");
+ return 1;
+ }
+ if (!strcmp(argv[1], "assert")) {
+ crashtype = 1;
+ } else if (!strcmp(argv[1], "crash")) {
+ crashtype = 0;
+ } else if (!strcmp(argv[1], "none")) {
+ crashtype = -1;
+ } else {
+ puts("Argument should be \"assert\" or \"crash\" or \"none\"");
+ return 1;
+ }
+
+ init_logging();
+ set_log_severity_config(LOG_WARN, LOG_ERR, &severity);
+ add_stream_log(&severity, "stdout", STDOUT_FILENO);
+ tor_log_update_sigsafe_err_fds();
+
+ configure_backtrace_handler(NULL);
+
+ signal(SIGABRT, abort_handler);
+
+ printf("%d\n", we_weave(2));
+
+ clean_up_backtrace_handler();
+
+ return 0;
+}
+
diff --git a/src/test/test_logging.c b/src/test/test_logging.c
new file mode 100644
index 000000000..7e558f83b
--- /dev/null
+++ b/src/test/test_logging.c
@@ -0,0 +1,135 @@
+/* Copyright (c) 2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "or.h"
+#include "torlog.h"
+#include "test.h"
+
+static void
+dummy_cb_fn(int severity, uint32_t domain, const char *msg)
+{
+ (void)severity; (void)domain; (void)msg;
+}
+
+static void
+test_get_sigsafe_err_fds(void *arg)
+{
+ const int *fds;
+ int n;
+ log_severity_list_t include_bug, no_bug, no_bug2;
+ (void) arg;
+ init_logging();
+
+ n = tor_log_get_sigsafe_err_fds(&fds);
+ tt_int_op(n, ==, 1);
+ tt_int_op(fds[0], ==, STDERR_FILENO);
+
+ set_log_severity_config(LOG_WARN, LOG_ERR, &include_bug);
+ set_log_severity_config(LOG_WARN, LOG_ERR, &no_bug);
+ no_bug.masks[0] &= ~(LD_BUG|LD_GENERAL);
+ set_log_severity_config(LOG_INFO, LOG_NOTICE, &no_bug2);
+
+ /* Add some logs; make sure the output is as expected. */
+ mark_logs_temp();
+ add_stream_log(&include_bug, "dummy-1", 3);
+ add_stream_log(&no_bug, "dummy-2", 4);
+ add_stream_log(&no_bug2, "dummy-3", 5);
+ add_callback_log(&include_bug, dummy_cb_fn);
+ close_temp_logs();
+ tor_log_update_sigsafe_err_fds();
+
+ n = tor_log_get_sigsafe_err_fds(&fds);
+ tt_int_op(n, ==, 2);
+ tt_int_op(fds[0], ==, STDERR_FILENO);
+ tt_int_op(fds[1], ==, 3);
+
+ /* Allow STDOUT to replace STDERR. */
+ add_stream_log(&include_bug, "dummy-4", STDOUT_FILENO);
+ tor_log_update_sigsafe_err_fds();
+ n = tor_log_get_sigsafe_err_fds(&fds);
+ tt_int_op(n, ==, 2);
+ tt_int_op(fds[0], ==, 3);
+ tt_int_op(fds[1], ==, STDOUT_FILENO);
+
+ /* But don't allow it to replace explicit STDERR. */
+ add_stream_log(&include_bug, "dummy-5", STDERR_FILENO);
+ tor_log_update_sigsafe_err_fds();
+ n = tor_log_get_sigsafe_err_fds(&fds);
+ tt_int_op(n, ==, 3);
+ tt_int_op(fds[0], ==, STDERR_FILENO);
+ tt_int_op(fds[1], ==, STDOUT_FILENO);
+ tt_int_op(fds[2], ==, 3);
+
+ /* Don't overflow the array. */
+ {
+ int i;
+ for (i=5; i<20; ++i) {
+ add_stream_log(&include_bug, "x-dummy", i);
+ }
+ }
+ tor_log_update_sigsafe_err_fds();
+ n = tor_log_get_sigsafe_err_fds(&fds);
+ tt_int_op(n, ==, 8);
+
+ done:
+ ;
+}
+
+static void
+test_sigsafe_err(void *arg)
+{
+ const char *fn=get_fname("sigsafe_err_log");
+ char *content=NULL;
+ log_severity_list_t include_bug;
+ smartlist_t *lines = smartlist_new();
+ (void)arg;
+
+ set_log_severity_config(LOG_WARN, LOG_ERR, &include_bug);
+
+ init_logging();
+ mark_logs_temp();
+ add_file_log(&include_bug, fn);
+ tor_log_update_sigsafe_err_fds();
+ close_temp_logs();
+
+ close(STDERR_FILENO);
+ log_err(LD_BUG, "Say, this isn't too cool.");
+ tor_log_err_sigsafe("Minimal.\n", NULL);
+
+ set_log_time_granularity(100*1000);
+ tor_log_err_sigsafe("Testing any ",
+ "attempt to manually log ",
+ "from a signal.\n",
+ NULL);
+ mark_logs_temp();
+ close_temp_logs();
+ close(STDERR_FILENO);
+ content = read_file_to_str(fn, 0, NULL);
+
+ tt_assert(content != NULL);
+ tor_split_lines(lines, content, (int)strlen(content));
+ tt_int_op(smartlist_len(lines), >=, 5);
+
+ if (strstr(smartlist_get(lines, 0), "opening new log file"))
+ smartlist_del_keeporder(lines, 0);
+ tt_assert(strstr(smartlist_get(lines, 0), "Say, this isn't too cool"));
+ /* Next line is blank. */
+ tt_assert(!strcmpstart(smartlist_get(lines, 1), "=============="));
+ tt_assert(!strcmpstart(smartlist_get(lines, 2), "Minimal."));
+ /* Next line is blank. */
+ tt_assert(!strcmpstart(smartlist_get(lines, 3), "=============="));
+ tt_str_op(smartlist_get(lines, 4), ==,
+ "Testing any attempt to manually log from a signal.");
+
+ done:
+ tor_free(content);
+ smartlist_free(lines);
+}
+
+struct testcase_t logging_tests[] = {
+ { "sigsafe_err_fds", test_get_sigsafe_err_fds, TT_FORK, NULL, NULL },
+ { "sigsafe_err", test_sigsafe_err, TT_FORK, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_util.c b/src/test/test_util.c
index ba58b31d4..ab5928ffd 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -2779,6 +2779,56 @@ test_util_format_hex_number(void *ptr)
}
/**
+ * Test for format_hex_number_sigsafe()
+ */
+
+static void
+test_util_format_dec_number(void *ptr)
+{
+ int i, len;
+ char buf[33];
+ const struct {
+ const char *str;
+ unsigned int x;
+ } test_data[] = {
+ {"0", 0},
+ {"1", 1},
+ {"1234", 1234},
+ {"12345678", 12345678},
+ {"99999999", 99999999},
+ {"100000000", 100000000},
+ {"4294967295", 4294967295u},
+#if UINT_MAX > 0xffffffff
+ {"18446744073709551615", 18446744073709551615u },
+#endif
+ {NULL, 0}
+ };
+
+ (void)ptr;
+
+ for (i = 0; test_data[i].str != NULL; ++i) {
+ len = format_dec_number_sigsafe(test_data[i].x, buf, sizeof(buf));
+ test_neq(len, 0);
+ test_eq(len, strlen(buf));
+ test_streq(buf, test_data[i].str);
+
+ len = format_dec_number_sigsafe(test_data[i].x, buf,
+ (int)(strlen(test_data[i].str) + 1));
+ test_eq(len, strlen(buf));
+ test_streq(buf, test_data[i].str);
+ }
+
+ test_eq(4, format_dec_number_sigsafe(7331, buf, 5));
+ test_streq(buf, "7331");
+ test_eq(0, format_dec_number_sigsafe(7331, buf, 4));
+ test_eq(1, format_dec_number_sigsafe(0, buf, 2));
+ test_eq(0, format_dec_number_sigsafe(0, buf, 1));
+
+ done:
+ return;
+}
+
+/**
* Test that we can properly format a Windows command line
*/
static void
@@ -3598,6 +3648,7 @@ struct testcase_t util_tests[] = {
UTIL_TEST(spawn_background_fail, 0),
UTIL_TEST(spawn_background_partial_read, 0),
UTIL_TEST(format_hex_number, 0),
+ UTIL_TEST(format_dec_number, 0),
UTIL_TEST(join_win_cmdline, 0),
UTIL_TEST(split_lines, 0),
UTIL_TEST(n_bits_set, 0),