diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/common/backtrace.c | 202 | ||||
-rw-r--r-- | src/common/backtrace.h | 12 | ||||
-rw-r--r-- | src/common/include.am | 2 | ||||
-rw-r--r-- | src/common/log.c | 140 | ||||
-rw-r--r-- | src/common/sandbox.c | 47 | ||||
-rw-r--r-- | src/common/sandbox.h | 3 | ||||
-rw-r--r-- | src/common/torlog.h | 8 | ||||
-rw-r--r-- | src/common/util.c | 111 | ||||
-rw-r--r-- | src/common/util.h | 17 | ||||
-rw-r--r-- | src/or/config.c | 3 | ||||
-rw-r--r-- | src/or/main.c | 10 | ||||
-rwxr-xr-x | src/test/bt_test.py | 42 | ||||
-rw-r--r-- | src/test/include.am | 12 | ||||
-rw-r--r-- | src/test/test.c | 3 | ||||
-rw-r--r-- | src/test/test_bt_cl.c | 110 | ||||
-rw-r--r-- | src/test/test_logging.c | 135 | ||||
-rw-r--r-- | src/test/test_util.c | 51 |
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), |