From 5343ee1a06ebb959fc77753898015186b94a5daa Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 19 Jul 2013 13:03:23 -0400 Subject: Add a signal-safe decimal formatting function --- src/common/util.c | 33 +++++++++++++++++++++++++++++++++ src/common/util.h | 1 + src/test/test_util.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) diff --git a/src/common/util.c b/src/common/util.c index 0e8d34eaf..25ea13371 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -3441,6 +3441,39 @@ format_hex_number_sigsafe(unsigned int x, char *buf, int buf_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) +{ + int len; + unsigned long tmp; + char *cp; + + tmp = x; + len = 1; + while (tmp > 9) { + tmp /= 10; + ++len; + } + + if (len >= buf_len) + return 0; + + cp = buf + len; + *cp = '\0'; + do { + unsigned digit = x % 10; + tor_assert(cp > buf); + --cp; + *cp = '0' + digit; + x /= 10; + } while (x); + + tor_assert(cp == buf); + + return len; +} + #ifndef _WIN32 /** Format child_state and saved_errno as a hex string placed in * hex_errno. Called between fork and _exit, so must be signal-handler diff --git a/src/common/util.h b/src/common/util.h index 0a8e4a23f..f6d828796 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -520,6 +520,7 @@ int32_t tor_weak_random_range(tor_weak_rng_t *rng, int32_t top); #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_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/test/test_util.c b/src/test/test_util.c index f7513c0f3..b331a804b 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -2670,6 +2670,56 @@ test_util_format_hex_number(void *ptr) return; } +/** + * 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 */ @@ -3362,6 +3412,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), -- cgit v1.2.3 From f6d8bc9389db72dc654560d0f86fd3edd3b14934 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 19 Jul 2013 13:40:20 -0400 Subject: Refactor the assertion-failure code into a function --- src/common/util.c | 15 ++++++++++++++- src/common/util.h | 14 +++++++------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/common/util.c b/src/common/util.c index 25ea13371..814eb1379 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -93,6 +93,20 @@ #include #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) +{ + log_err(LD_BUG, "%s:%u: %s: Assertion %s failed; aborting.", + fname, line, func, expr); + fprintf(stderr,"%s:%u: %s: Assertion %s failed; aborting.\n", + fname, line, func, expr); +} + /* ===== * Memory management * ===== */ @@ -5057,4 +5071,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 f6d828796..1c92c4f5f 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. -- cgit v1.2.3 From 063bea58bcc1c27864a0351bba07254855903377 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 19 Jul 2013 14:09:58 -0400 Subject: Basic backtrace ability On platforms with the backtrace/backtrace_symbols_fd interface, Tor can now dump stack traces on assertion failure. By default, I log them to DataDir/stack_dump and to stderr. --- changes/stack_trace | 6 ++ configure.ac | 4 ++ src/common/backtrace.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++++ src/common/backtrace.h | 12 ++++ src/common/include.am | 2 + src/common/util.c | 6 ++ src/or/config.c | 14 +++++ src/or/main.c | 9 ++- 8 files changed, 207 insertions(+), 3 deletions(-) create mode 100644 changes/stack_trace create mode 100644 src/common/backtrace.c create mode 100644 src/common/backtrace.h diff --git a/changes/stack_trace b/changes/stack_trace new file mode 100644 index 000000000..dc6363887 --- /dev/null +++ b/changes/stack_trace @@ -0,0 +1,6 @@ + o Major features: + - On some platforms (currently, recent OSX versions, and glibc-based + platforms that support the ELF format), Tor can now dump + stack traces when an assertion fails. By default, traces are dumped + to stderr, and to a stack_dump file in the DataDirectory. + diff --git a/configure.ac b/configure.ac index 4aeec929e..5059079f7 100644 --- a/configure.ac +++ b/configure.ac @@ -302,6 +302,8 @@ dnl exports strlcpy without defining it in a header. AC_CHECK_FUNCS( _NSGetEnviron \ accept4 \ + backtrace \ + backtrace_symbols_fd \ clock_gettime \ flock \ ftime \ @@ -319,6 +321,7 @@ AC_CHECK_FUNCS( memmem \ prctl \ rint \ + sigaction \ socketpair \ strlcat \ strlcpy \ @@ -802,6 +805,7 @@ dnl These headers are not essential AC_CHECK_HEADERS( arpa/inet.h \ crt_externs.h \ + execinfo.h \ grp.h \ ifaddrs.h \ inttypes.h \ diff --git a/src/common/backtrace.c b/src/common/backtrace.c new file mode 100644 index 000000000..d3f59b2c7 --- /dev/null +++ b/src/common/backtrace.c @@ -0,0 +1,157 @@ +/* Copyright (c) 2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#include "backtrace.h" +#include "compat.h" +#include "util.h" + +#ifdef HAVE_EXECINFO_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#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 + +static char *bt_filename = NULL; +static char *bt_version = NULL; + +#ifndef NO_BACKTRACE_IMPL +/**DOCDOC*/ +static int +open_bt_target(void) +{ + int fd = -1; + if (bt_filename) + fd = open(bt_filename, O_WRONLY|O_CREAT|O_APPEND, 0700); + return fd; +} +#endif + +/**DOCDOC*/ +static void +bt_write(int fd, const char *s, ssize_t n) +{ + int r; + if (n < 0) n = strlen(s); + + r = write(STDERR_FILENO, s, n); + if (fd >= 0) + r = write(fd, s, n); + (void)r; +} + +#ifdef USE_BACKTRACE +#define MAX_DEPTH 256 +static void *cb_buf[MAX_DEPTH]; + +/**DOCDOC*/ +void +dump_backtrace(const char *msg) +{ + char timebuf[32]; + time_t t = time(NULL); + int timebuf_len; + int depth; + int fd; + if (!msg) msg = "unspecified crash"; + + depth = backtrace(cb_buf, MAX_DEPTH); + + t /= 900; t *= 900; /* Round to the previous 15 minutes */ + timebuf[0] = '\0'; + timebuf_len = format_dec_number_sigsafe(t, timebuf, sizeof(timebuf)); + + fd = open_bt_target(); + bt_write(fd, "========================================" + "====================================\n", -1); + bt_write(fd, bt_version, -1); + bt_write(fd, " died around T=", -1); + bt_write(fd, timebuf, timebuf_len); + bt_write(fd, ": ", 2); + bt_write(fd, msg, -1); + bt_write(fd, "\n", 1); + backtrace_symbols_fd(cb_buf, depth, STDERR_FILENO); + if (fd >= 0) + backtrace_symbols_fd(cb_buf, depth, fd); + + close(fd); +} + +/**DOCDOC*/ +static int +install_bt_handler(void) +{ + /*XXXX add signal handlers */ + /*XXXX make this idempotent */ + return 0; +} +/**DOCDOC*/ +static void +remove_bt_handler(void) +{ +} +#endif + +#ifdef NO_BACKTRACE_IMPL +/**DOCDOC*/ +void +dump_backtrace(const char *msg) +{ + bt_write(-1, bt_version, -1); + bt_write(-1, " died: ", -1); + bt_write(-1, msg, -1); + bt_write(-1, "\n", -1); +} + +/**DOCDOC*/ +static int +install_bt_handler(void) +{ + return 0; +} + +/**DOCDOC*/ +static void +remove_bt_handler(void) +{ +} +#endif + +/**DOCDOC*/ +int +configure_backtrace_handler(const char *filename, const char *tor_version) +{ + tor_free(bt_filename); + if (filename) + bt_filename = tor_strdup(filename); + tor_free(bt_version); + if (!tor_version) + tor_version = "Tor"; + bt_version = tor_strdup(tor_version); + + return install_bt_handler(); +} + +/**DOCDOC*/ +void +clean_up_backtrace_handler(void) +{ + remove_bt_handler(); + + tor_free(bt_filename); + tor_free(bt_version); +} + diff --git a/src/common/backtrace.h b/src/common/backtrace.h new file mode 100644 index 000000000..bb2396080 --- /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 dump_backtrace(const char *msg); +int configure_backtrace_handler(const char *filename, 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/util.c b/src/common/util.c index 814eb1379..0b65437b0 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -24,6 +24,7 @@ #include "torint.h" #include "container.h" #include "address.h" +#include "backtrace.h" #ifdef _WIN32 #include @@ -101,8 +102,13 @@ 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); + dump_backtrace(buf); fprintf(stderr,"%s:%u: %s: Assertion %s failed; aborting.\n", fname, line, func, expr); } diff --git a/src/or/config.c b/src/or/config.c index ad6689cee..af5bf41d2 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -12,6 +12,7 @@ #define CONFIG_PRIVATE #include "or.h" #include "addressmap.h" +#include "backtrace.h" #include "channel.h" #include "circuitbuild.h" #include "circuitlist.h" @@ -1113,6 +1114,19 @@ options_act_reversible(const or_options_t *old_options, char **msg) /* No need to roll back, since you can't change the value. */ } + /* Enable crash logging to files */ + { + /* XXXX we might want to set this up earlier, if possible! */ + char *backtrace_fname = NULL; + char *progname = NULL; + tor_asprintf(&backtrace_fname, "%s"PATH_SEPARATOR"stack_dump", + options->DataDirectory); + tor_asprintf(&progname, "Tor %s", get_version()); + configure_backtrace_handler(backtrace_fname, progname); + tor_free(backtrace_fname); + tor_free(progname); + } + /* Write control ports to disk as appropriate */ control_ports_write_to_file(); diff --git a/src/or/main.c b/src/or/main.c index d1728250a..ea86df073 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -12,6 +12,7 @@ #include "or.h" #include "addressmap.h" +#include "backtrace.h" #include "buffers.h" #include "channel.h" #include "channeltls.h" @@ -2304,7 +2305,7 @@ handle_signals(int is_parent) int tor_init(int argc, char *argv[]) { - char buf[256]; + char progname[256]; int i, quiet = 0; time_of_process_start = time(NULL); if (!connection_array) @@ -2314,8 +2315,8 @@ tor_init(int argc, char *argv[]) if (!active_linked_connection_lst) active_linked_connection_lst = smartlist_new(); /* 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. */ @@ -2684,6 +2685,8 @@ tor_main(int argc, char *argv[]) } #endif + configure_backtrace_handler(NULL, get_version()); + update_approx_time(time(NULL)); tor_threads_init(); init_logging(); -- cgit v1.2.3 From b0023083c413c6447ef34cf3c6cfaf54a1cc8793 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 19 Jul 2013 14:18:43 -0400 Subject: On Linux (and some other systems) we need -rdynamic for backtraces --- configure.ac | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configure.ac b/configure.ac index 5059079f7..8e247db32 100644 --- a/configure.ac +++ b/configure.ac @@ -593,6 +593,9 @@ if test x$enable_linker_hardening != xno; then TOR_CHECK_LDFLAGS(-z relro -z now, "$all_ldflags_for_check", "$all_libs_for_check") fi +# For backtrace support +TOR_CHECK_LDFLAGS(-rdynamic) + dnl ------------------------------------------------------ dnl Now see if we have a -fomit-frame-pointer compiler option. -- cgit v1.2.3 From bd8ad674b913582b6f8e5b85ac722e14598d681b Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 19 Jul 2013 22:47:49 -0400 Subject: Add a sighandler-safe logging mechanism We had accidentially grown two fake ones: one for backtrace.c, and one for sandbox.c. Let's do this properly instead. Now, when we configure logs, we keep track of fds that should get told about bad stuff happening from signal handlers. There's another entry point for these that avoids using non-signal-handler-safe functions. --- src/common/backtrace.c | 69 +++++---------------------- src/common/backtrace.h | 2 +- src/common/log.c | 125 ++++++++++++++++++++++++++++++++++++++++++------- src/common/sandbox.c | 47 +++---------------- src/common/sandbox.h | 1 - src/common/torlog.h | 5 +- src/or/config.c | 16 +------ src/or/main.c | 2 +- 8 files changed, 135 insertions(+), 132 deletions(-) diff --git a/src/common/backtrace.c b/src/common/backtrace.c index d3f59b2c7..2c49146a0 100644 --- a/src/common/backtrace.c +++ b/src/common/backtrace.c @@ -5,6 +5,7 @@ #include "backtrace.h" #include "compat.h" #include "util.h" +#include "torlog.h" #ifdef HAVE_EXECINFO_H #include @@ -25,34 +26,8 @@ #define NO_BACKTRACE_IMPL #endif -static char *bt_filename = NULL; static char *bt_version = NULL; -#ifndef NO_BACKTRACE_IMPL -/**DOCDOC*/ -static int -open_bt_target(void) -{ - int fd = -1; - if (bt_filename) - fd = open(bt_filename, O_WRONLY|O_CREAT|O_APPEND, 0700); - return fd; -} -#endif - -/**DOCDOC*/ -static void -bt_write(int fd, const char *s, ssize_t n) -{ - int r; - if (n < 0) n = strlen(s); - - r = write(STDERR_FILENO, s, n); - if (fd >= 0) - r = write(fd, s, n); - (void)r; -} - #ifdef USE_BACKTRACE #define MAX_DEPTH 256 static void *cb_buf[MAX_DEPTH]; @@ -61,33 +36,20 @@ static void *cb_buf[MAX_DEPTH]; void dump_backtrace(const char *msg) { - char timebuf[32]; - time_t t = time(NULL); - int timebuf_len; int depth; - int fd; + const int *fds; + int n_fds; + int i; + if (!msg) msg = "unspecified crash"; depth = backtrace(cb_buf, MAX_DEPTH); - t /= 900; t *= 900; /* Round to the previous 15 minutes */ - timebuf[0] = '\0'; - timebuf_len = format_dec_number_sigsafe(t, timebuf, sizeof(timebuf)); - - fd = open_bt_target(); - bt_write(fd, "========================================" - "====================================\n", -1); - bt_write(fd, bt_version, -1); - bt_write(fd, " died around T=", -1); - bt_write(fd, timebuf, timebuf_len); - bt_write(fd, ": ", 2); - bt_write(fd, msg, -1); - bt_write(fd, "\n", 1); - backtrace_symbols_fd(cb_buf, depth, STDERR_FILENO); - if (fd >= 0) - backtrace_symbols_fd(cb_buf, depth, fd); - - close(fd); + tor_log_err_sigsafe(bt_version, " died: ", msg, "\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]); } /**DOCDOC*/ @@ -110,10 +72,7 @@ remove_bt_handler(void) void dump_backtrace(const char *msg) { - bt_write(-1, bt_version, -1); - bt_write(-1, " died: ", -1); - bt_write(-1, msg, -1); - bt_write(-1, "\n", -1); + tor_log_err_sigsafe(bt_version, " died: ", msg, "\n", NULL); } /**DOCDOC*/ @@ -132,11 +91,8 @@ remove_bt_handler(void) /**DOCDOC*/ int -configure_backtrace_handler(const char *filename, const char *tor_version) +configure_backtrace_handler(const char *tor_version) { - tor_free(bt_filename); - if (filename) - bt_filename = tor_strdup(filename); tor_free(bt_version); if (!tor_version) tor_version = "Tor"; @@ -151,7 +107,6 @@ clean_up_backtrace_handler(void) { remove_bt_handler(); - tor_free(bt_filename); tor_free(bt_version); } diff --git a/src/common/backtrace.h b/src/common/backtrace.h index bb2396080..7d7c10a80 100644 --- a/src/common/backtrace.h +++ b/src/common/backtrace.h @@ -5,7 +5,7 @@ #define TOR_BACKTRACE_H void dump_backtrace(const char *msg); -int configure_backtrace_handler(const char *filename, const char *tor_version); +int configure_backtrace_handler(const char *tor_version); void clean_up_backtrace_handler(void); #endif diff --git a/src/common/log.c b/src/common/log.c index 303fba93a..3ce4df190 100644 --- a/src/common/log.c +++ b/src/common/log.c @@ -443,6 +443,115 @@ 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 s 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 *out 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 n-element array array + * contains item. */ +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; + + LOCK_LOGS(); + /* Always try 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)) { + /* 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; + } + } + UNLOCK_LOGS(); +} + /** Output a message to the log, prefixed with a function name fn. */ #ifdef __GNUC__ /** GCC-based implementation of the log_fn backend, used when we have @@ -1142,22 +1251,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 dbb1657cd..a37c74e4a 100644 --- a/src/common/sandbox.c +++ b/src/common/sandbox.c @@ -184,16 +184,6 @@ install_glob_syscall_filter(void) return (rc < 0 ? -rc : rc); } -/** Additional file descriptor to use when logging seccomp2 failures */ -static int sigsys_debugging_fd = -1; - -/** Use the file descriptor fd 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 @@ -203,8 +193,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) @@ -215,24 +205,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); @@ -318,14 +295,4 @@ tor_global_sandbox(void) #endif } -/** Use fd to log non-survivable sandbox violations. */ -void -sandbox_set_debugging_fd(int fd) -{ -#ifdef USE_LIBSECCOMP - sigsys_set_debugging_fd(fd); -#else - (void)fd; -#endif -} diff --git a/src/common/sandbox.h b/src/common/sandbox.h index bd6f0cfb4..54f16ec86 100644 --- a/src/common/sandbox.h +++ b/src/common/sandbox.h @@ -48,7 +48,6 @@ #endif // __linux__ -void sandbox_set_debugging_fd(int fd); int tor_global_sandbox(void); #endif /* SANDBOX_H_ */ diff --git a/src/common/torlog.h b/src/common/torlog.h index ecd7e121e..903237940 100644 --- a/src/common/torlog.h +++ b/src/common/torlog.h @@ -136,7 +136,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 +148,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/or/config.c b/src/or/config.c index af5bf41d2..c62a25a7a 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -12,7 +12,6 @@ #define CONFIG_PRIVATE #include "or.h" #include "addressmap.h" -#include "backtrace.h" #include "channel.h" #include "circuitbuild.h" #include "circuitlist.h" @@ -1114,19 +1113,6 @@ options_act_reversible(const or_options_t *old_options, char **msg) /* No need to roll back, since you can't change the value. */ } - /* Enable crash logging to files */ - { - /* XXXX we might want to set this up earlier, if possible! */ - char *backtrace_fname = NULL; - char *progname = NULL; - tor_asprintf(&backtrace_fname, "%s"PATH_SEPARATOR"stack_dump", - options->DataDirectory); - tor_asprintf(&progname, "Tor %s", get_version()); - configure_backtrace_handler(backtrace_fname, progname); - tor_free(backtrace_fname); - tor_free(progname); - } - /* Write control ports to disk as appropriate */ control_ports_write_to_file(); @@ -1156,7 +1142,7 @@ options_act_reversible(const or_options_t *old_options, char **msg) goto rollback; } - sandbox_set_debugging_fd(get_err_logging_fd()); + tor_log_update_sigsafe_err_fds(); commit: r = 0; diff --git a/src/or/main.c b/src/or/main.c index ea86df073..a8f1c2306 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -2685,7 +2685,7 @@ tor_main(int argc, char *argv[]) } #endif - configure_backtrace_handler(NULL, get_version()); + configure_backtrace_handler(get_version()); update_approx_time(time(NULL)); tor_threads_init(); -- cgit v1.2.3 From ce8ae49c9437086a886af631d3f618ec338637d0 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 25 Jul 2013 12:12:35 +0200 Subject: Improve new assertion message logging Don't report that a failure happened in the assertion_failed function just because we logged it from there. --- src/common/log.c | 19 +++++++++++++++---- src/common/torlog.h | 3 +++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/common/log.c b/src/common/log.c index 3ce4df190..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; @@ -525,10 +525,11 @@ void tor_log_update_sigsafe_err_fds(void) { const logfile_t *lf; + int found_real_stderr = 0; LOCK_LOGS(); - /* Always try for stderr. This is safe because when we daemonize, we dup2 - * /dev/null to stderr, */ + /* 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; @@ -541,6 +542,8 @@ tor_log_update_sigsafe_err_fds(void) 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; @@ -549,6 +552,14 @@ tor_log_update_sigsafe_err_fds(void) 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(); } diff --git a/src/common/torlog.h b/src/common/torlog.h index 903237940..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; -- cgit v1.2.3 From 2662885aa9cb6c9b7447b729d0321a633e8c46c3 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 26 Jul 2013 12:46:17 +0200 Subject: Use pc_from_ucontext.m4 from Google Performance Tools This M4 module lets us learn the right way (out of at least 18 possibilities) to extract the current PC for stack-trace-fixup-in-signal purposes. The Google Performance Tools license is 3-clause BSD. --- LICENSE | 33 +++++++++++++ configure.ac | 2 + m4/pc_from_ucontext.m4 | 131 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+) create mode 100644 m4/pc_from_ucontext.m4 diff --git a/LICENSE b/LICENSE index 4ed3bd8da..8a6fccaf4 100644 --- a/LICENSE +++ b/LICENSE @@ -134,6 +134,39 @@ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS DATABASE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +=============================================================================== +m4/pc_from_ucontext.m4 is available under the following license. Note that +it is *not* built into the Tor license. + +Copyright (c) 2005, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + =============================================================================== If you got Tor as a static binary with OpenSSL included, then you should know: "This product includes software developed by the OpenSSL Project diff --git a/configure.ac b/configure.ac index 8e247db32..06e9e3b37 100644 --- a/configure.ac +++ b/configure.ac @@ -937,6 +937,8 @@ AC_CHECK_SIZEOF(pid_t) AC_CHECK_TYPES([uint, u_char, ssize_t]) +AC_PC_FROM_UCONTEXT([/bin/true]) + dnl used to include sockaddr_storage, but everybody has that. AC_CHECK_TYPES([struct in6_addr, struct sockaddr_in6, sa_family_t], , , [#ifdef HAVE_SYS_TYPES_H diff --git a/m4/pc_from_ucontext.m4 b/m4/pc_from_ucontext.m4 new file mode 100644 index 000000000..6bedcbb2d --- /dev/null +++ b/m4/pc_from_ucontext.m4 @@ -0,0 +1,131 @@ +# This file is from Google Performance Tools, svn revision r226. +# +# The Google Performance Tools license is: +######## +# Copyright (c) 2005, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +######## +# Original file follows below. + +# We want to access the "PC" (Program Counter) register from a struct +# ucontext. Every system has its own way of doing that. We try all the +# possibilities we know about. Note REG_PC should come first (REG_RIP +# is also defined on solaris, but does the wrong thing). + +# OpenBSD doesn't have ucontext.h, but we can get PC from ucontext_t +# by using signal.h. + +# The first argument of AC_PC_FROM_UCONTEXT will be invoked when we +# cannot find a way to obtain PC from ucontext. + +AC_DEFUN([AC_PC_FROM_UCONTEXT], + [AC_CHECK_HEADERS(ucontext.h) + # Redhat 7 has , but it barfs if we #include it directly + # (this was fixed in later redhats). works fine, so use that. + if grep "Red Hat Linux release 7" /etc/redhat-release >/dev/null 2>&1; then + AC_DEFINE(HAVE_SYS_UCONTEXT_H, 0, [ is broken on redhat 7]) + ac_cv_header_sys_ucontext_h=no + else + AC_CHECK_HEADERS(sys/ucontext.h) # ucontext on OS X 10.6 (at least) + fi + AC_CHECK_HEADERS(cygwin/signal.h) # ucontext on cywgin + AC_MSG_CHECKING([how to access the program counter from a struct ucontext]) + pc_fields=" uc_mcontext.gregs[[REG_PC]]" # Solaris x86 (32 + 64 bit) + pc_fields="$pc_fields uc_mcontext.gregs[[REG_EIP]]" # Linux (i386) + pc_fields="$pc_fields uc_mcontext.gregs[[REG_RIP]]" # Linux (x86_64) + pc_fields="$pc_fields uc_mcontext.sc_ip" # Linux (ia64) + pc_fields="$pc_fields uc_mcontext.uc_regs->gregs[[PT_NIP]]" # Linux (ppc) + pc_fields="$pc_fields uc_mcontext.gregs[[R15]]" # Linux (arm old [untested]) + pc_fields="$pc_fields uc_mcontext.arm_pc" # Linux (arm arch 5) + pc_fields="$pc_fields uc_mcontext.gp_regs[[PT_NIP]]" # Suse SLES 11 (ppc64) + pc_fields="$pc_fields uc_mcontext.mc_eip" # FreeBSD (i386) + pc_fields="$pc_fields uc_mcontext.mc_rip" # FreeBSD (x86_64 [untested]) + pc_fields="$pc_fields uc_mcontext.__gregs[[_REG_EIP]]" # NetBSD (i386) + pc_fields="$pc_fields uc_mcontext.__gregs[[_REG_RIP]]" # NetBSD (x86_64) + pc_fields="$pc_fields uc_mcontext->ss.eip" # OS X (i386, <=10.4) + pc_fields="$pc_fields uc_mcontext->__ss.__eip" # OS X (i386, >=10.5) + pc_fields="$pc_fields uc_mcontext->ss.rip" # OS X (x86_64) + pc_fields="$pc_fields uc_mcontext->__ss.__rip" # OS X (>=10.5 [untested]) + pc_fields="$pc_fields uc_mcontext->ss.srr0" # OS X (ppc, ppc64 [untested]) + pc_fields="$pc_fields uc_mcontext->__ss.__srr0" # OS X (>=10.5 [untested]) + pc_field_found=false + for pc_field in $pc_fields; do + if ! $pc_field_found; then + # Prefer sys/ucontext.h to ucontext.h, for OS X's sake. + if test "x$ac_cv_header_cygwin_signal_h" = xyes; then + AC_TRY_COMPILE([#define _GNU_SOURCE 1 + #include ], + [ucontext_t u; return u.$pc_field == 0;], + AC_DEFINE_UNQUOTED(PC_FROM_UCONTEXT, $pc_field, + How to access the PC from a struct ucontext) + AC_MSG_RESULT([$pc_field]) + pc_field_found=true) + elif test "x$ac_cv_header_sys_ucontext_h" = xyes; then + AC_TRY_COMPILE([#define _GNU_SOURCE 1 + #include ], + [ucontext_t u; return u.$pc_field == 0;], + AC_DEFINE_UNQUOTED(PC_FROM_UCONTEXT, $pc_field, + How to access the PC from a struct ucontext) + AC_MSG_RESULT([$pc_field]) + pc_field_found=true) + elif test "x$ac_cv_header_ucontext_h" = xyes; then + AC_TRY_COMPILE([#define _GNU_SOURCE 1 + #include ], + [ucontext_t u; return u.$pc_field == 0;], + AC_DEFINE_UNQUOTED(PC_FROM_UCONTEXT, $pc_field, + How to access the PC from a struct ucontext) + AC_MSG_RESULT([$pc_field]) + pc_field_found=true) + else # hope some standard header gives it to us + AC_TRY_COMPILE([], + [ucontext_t u; return u.$pc_field == 0;], + AC_DEFINE_UNQUOTED(PC_FROM_UCONTEXT, $pc_field, + How to access the PC from a struct ucontext) + AC_MSG_RESULT([$pc_field]) + pc_field_found=true) + fi + fi + done + if ! $pc_field_found; then + pc_fields=" sc_eip" # OpenBSD (i386) + pc_fields="$pc_fields sc_rip" # OpenBSD (x86_64) + for pc_field in $pc_fields; do + if ! $pc_field_found; then + AC_TRY_COMPILE([#include ], + [ucontext_t u; return u.$pc_field == 0;], + AC_DEFINE_UNQUOTED(PC_FROM_UCONTEXT, $pc_field, + How to access the PC from a struct ucontext) + AC_MSG_RESULT([$pc_field]) + pc_field_found=true) + fi + done + fi + if ! $pc_field_found; then + [$1] + fi]) -- cgit v1.2.3 From d631ddfb59eca910d1faf45a711da630bab7c89a Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 26 Jul 2013 13:22:56 +0200 Subject: Make backtrace handler handle signals correctly. This meant moving a fair bit of code around, and writing a signal cleanup function. Still pretty nice from what I can tell, though. --- src/common/backtrace.c | 125 ++++++++++++++++++++++++++++++++++++++++++------- src/common/backtrace.h | 2 +- src/common/util.c | 4 +- 3 files changed, 111 insertions(+), 20 deletions(-) diff --git a/src/common/backtrace.c b/src/common/backtrace.c index 2c49146a0..9c69e4f3f 100644 --- a/src/common/backtrace.c +++ b/src/common/backtrace.c @@ -7,6 +7,8 @@ #include "util.h" #include "torlog.h" +#define __USE_GNU + #ifdef HAVE_EXECINFO_H #include #endif @@ -16,6 +18,15 @@ #ifdef HAVE_UNISTD_H #include #endif +#ifdef HAVE_SIGNAL_H +#include +#endif +#ifdef HAVE_UCONTEXT_H +#include +#endif +#ifdef HAVE_SYS_UCONTEXT_H +#include +#endif #if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && \ defined(HAVE_BACKTRACE_SYMBOLS_FD) && defined(HAVE_SIGACTION) @@ -26,53 +37,135 @@ #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]; -/**DOCDOC*/ +/** Change a stacktrace in stack of depth depth so that it will + * log the correct function from which a signal was received with context + * ctx. (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 msg at severity in domain, and follow + * that with a backtrace log. */ void -dump_backtrace(const char *msg) +log_backtrace(int severity, int domain, const char *msg) { - int depth; - const int *fds; - int n_fds; + 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); +} - if (!msg) msg = "unspecified crash"; +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: ", msg, "\n", + 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(); } -/**DOCDOC*/ +/** 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) { - /*XXXX add signal handlers */ /*XXXX make this idempotent */ - return 0; + 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; } -/**DOCDOC*/ + +/** Uninstall crash handlers. */ static void remove_bt_handler(void) { + /*XXXX writeme*/ } #endif #ifdef NO_BACKTRACE_IMPL -/**DOCDOC*/ +/**DOCDOC */ void -dump_backtrace(const char *msg) +log_backtrace(int severity, int domain, const char *msg) { - tor_log_err_sigsafe(bt_version, " died: ", msg, "\n", NULL); + tor_log(severity, domain, "%s. (Stack trace not available)", msg); } /**DOCDOC*/ @@ -95,8 +188,8 @@ configure_backtrace_handler(const char *tor_version) { tor_free(bt_version); if (!tor_version) - tor_version = "Tor"; - bt_version = tor_strdup(tor_version); + tor_version = ""; + tor_asprintf(&bt_version, "Tor %s", tor_version); return install_bt_handler(); } diff --git a/src/common/backtrace.h b/src/common/backtrace.h index 7d7c10a80..765436fee 100644 --- a/src/common/backtrace.h +++ b/src/common/backtrace.h @@ -4,7 +4,7 @@ #ifndef TOR_BACKTRACE_H #define TOR_BACKTRACE_H -void dump_backtrace(const char *msg); +void log_backtrace(int severity, int domain, const char *msg); int configure_backtrace_handler(const char *tor_version); void clean_up_backtrace_handler(void); diff --git a/src/common/util.c b/src/common/util.c index 0b65437b0..bbb604492 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -108,9 +108,7 @@ tor_assertion_failed_(const char *fname, unsigned int line, tor_snprintf(buf, sizeof(buf), "Assertion %s failed in %s at %s:%u", expr, func, fname, line); - dump_backtrace(buf); - fprintf(stderr,"%s:%u: %s: Assertion %s failed; aborting.\n", - fname, line, func, expr); + log_backtrace(LOG_ERR, LD_BUG, buf); } /* ===== -- cgit v1.2.3 From 0cf234317f4dd6395490f24f12a7d35a480ddf11 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 29 Jul 2013 13:30:49 -0400 Subject: Unit tests for new functions in log.c --- src/or/config.c | 3 +- src/test/include.am | 1 + src/test/test.c | 3 ++ src/test/test_logging.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 src/test/test_logging.c diff --git a/src/or/config.c b/src/or/config.c index c62a25a7a..fc033ce1b 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -1142,8 +1142,6 @@ options_act_reversible(const or_options_t *old_options, char **msg) goto rollback; } - tor_log_update_sigsafe_err_fds(); - commit: r = 0; if (logs_marked) { @@ -1153,6 +1151,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/test/include.am b/src/test/include.am index fb704d718..ffbfefba1 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -27,6 +27,7 @@ src_test_test_SOURCES = \ src/test/test_data.c \ src/test/test_dir.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 \ diff --git a/src/test/test.c b/src/test/test.c index 60fbfb132..ae4ab4462 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -2136,6 +2136,8 @@ extern struct testcase_t circuitlist_tests[]; extern struct testcase_t circuitmux_tests[]; extern struct testcase_t cell_queue_tests[]; extern struct testcase_t options_tests[]; +extern struct testcase_t logging_tests[]; +extern struct testcase_t backtrace_tests[]; static struct testgroup_t testgroups[] = { { "", test_array }, @@ -2144,6 +2146,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_logging.c b/src/test/test_logging.c new file mode 100644 index 000000000..4111a46eb --- /dev/null +++ b/src/test/test_logging.c @@ -0,0 +1,133 @@ +/* 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); + 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 +}; + -- cgit v1.2.3 From 56e3f056e9a03015aa55392a8028e2cbe097a0fb Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 29 Jul 2013 21:56:31 -0400 Subject: Tests for backtrace.c These need to be a separate executable, since the point of backtrace.c is that it can crash and write stuff. --- .gitignore | 2 + src/test/bt_test.py | 42 ++++++++++++++++++ src/test/include.am | 8 ++++ src/test/test_bt_cl.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++ src/test/test_logging.c | 2 + 5 files changed, 164 insertions(+) create mode 100755 src/test/bt_test.py create mode 100644 src/test/test_bt_cl.c diff --git a/.gitignore b/.gitignore index 201d04da6..a301acc2c 100644 --- a/.gitignore +++ b/.gitignore @@ -164,9 +164,11 @@ /src/test/bench /src/test/bench.exe /src/test/test +/src/test/test-bt-cl /src/test/test-child /src/test/test-ntor-cl /src/test/test.exe +/src/test/test-bt-cl.exe /src/test/test-child.exe /src/test/test-ntor-cl.exe 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 ffbfefba1..e879c2000 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -77,3 +77,11 @@ src_test_test_ntor_cl_AM_CPPFLAGS = \ 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) + 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 +#include + +#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 index 4111a46eb..7e558f83b 100644 --- a/src/test/test_logging.c +++ b/src/test/test_logging.c @@ -96,6 +96,8 @@ test_sigsafe_err(void *arg) 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", -- cgit v1.2.3 From c3ea946839704c4f1f0369a1e91bdb749991e346 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 15 Aug 2013 12:45:46 -0400 Subject: Reseolve DOCDOC and XXXXs in backtrace.c --- src/common/backtrace.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/common/backtrace.c b/src/common/backtrace.c index 9c69e4f3f..78bc66efb 100644 --- a/src/common/backtrace.c +++ b/src/common/backtrace.c @@ -132,7 +132,6 @@ crash_handler(int sig, siginfo_t *si, void *ctx_) static int install_bt_handler(void) { - /*XXXX make this idempotent */ int trap_signals[] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGSYS, SIGIO, -1 }; int i, rv=0; @@ -156,33 +155,30 @@ install_bt_handler(void) static void remove_bt_handler(void) { - /*XXXX writeme*/ + /* We don't need to actually free anything at exit here. */ } #endif #ifdef NO_BACKTRACE_IMPL -/**DOCDOC */ void log_backtrace(int severity, int domain, const char *msg) { tor_log(severity, domain, "%s. (Stack trace not available)", msg); } -/**DOCDOC*/ static int install_bt_handler(void) { return 0; } -/**DOCDOC*/ static void remove_bt_handler(void) { } #endif -/**DOCDOC*/ +/** Set up code to handle generating error messages on crashes. */ int configure_backtrace_handler(const char *tor_version) { @@ -194,7 +190,8 @@ configure_backtrace_handler(const char *tor_version) return install_bt_handler(); } -/**DOCDOC*/ +/** Perform end-of-process cleanup for code that generates error messages on + * crashes. */ void clean_up_backtrace_handler(void) { -- cgit v1.2.3 From c2dfae78d3a881af7cfe2fb4a79da7e3598788fc Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 18 Oct 2013 10:37:23 -0700 Subject: Refactor format_*_number_sigsafe to have a common implementation --- src/common/util.c | 113 +++++++++++++++++++++++------------------------------- src/common/util.h | 2 +- 2 files changed, 49 insertions(+), 66 deletions(-) diff --git a/src/common/util.c b/src/common/util.c index bbb604492..cb23e7e9a 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -3396,6 +3396,51 @@ tor_join_win_cmdline(const char *argv[]) return joined_argv; } +/* As format_{hex,dex}_number_sigsafe, but takes a radix 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. * @@ -3418,78 +3463,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 len */ - return 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) { - int len; - unsigned long tmp; - char *cp; - - tmp = x; - len = 1; - while (tmp > 9) { - tmp /= 10; - ++len; - } - - if (len >= buf_len) - return 0; - - cp = buf + len; - *cp = '\0'; - do { - unsigned digit = x % 10; - tor_assert(cp > buf); - --cp; - *cp = '0' + digit; - x /= 10; - } while (x); - - tor_assert(cp == buf); - - return len; + return format_number_sigsafe(x, buf, buf_len, 10); } #ifndef _WIN32 diff --git a/src/common/util.h b/src/common/util.h index 1c92c4f5f..37effa258 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -519,7 +519,7 @@ int32_t tor_weak_random_range(tor_weak_rng_t *rng, int32_t top); * n */ #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 -- cgit v1.2.3 From c81f64ab44f71a97649a44e10d6fcbf15aa8c835 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 18 Nov 2013 10:48:08 -0500 Subject: Improve backtrace changes file --- changes/stack_trace | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/changes/stack_trace b/changes/stack_trace index dc6363887..2eaf15ec8 100644 --- a/changes/stack_trace +++ b/changes/stack_trace @@ -1,6 +1,8 @@ o Major features: - - On some platforms (currently, recent OSX versions, and glibc-based - platforms that support the ELF format), Tor can now dump - stack traces when an assertion fails. By default, traces are dumped - to stderr, and to a stack_dump file in the DataDirectory. + - On some platforms (currently: recent OSX versions, glibc-based + platforms that support the ELF format, and a few other + Unix-like operating systems), Tor can now dump stack traces + when a crash occurs or an assertion fails. By default, traces + are dumped to stderr (if possible) and to any logs that are + reporting errors. -- cgit v1.2.3