commit 5ecd1fec1583b8aa865ac5cae0cece42451395bd Author: Nick Mathewson nickm@torproject.org Date: Wed Jun 20 13:02:05 2018 -0400
Move horrible-emergency handling into torerr.[ch]
Previously we had code like this for bad things happening from signal handlers, but it makes sense to use the same logic to handle cases when something is happening at a level too low for log.c to be involved.
My raw_assert*() stuff now uses this code. --- src/common/backtrace.c | 2 +- src/common/compat_time.c | 1 + src/common/include.am | 2 + src/common/log.c | 89 ++++--------------- src/common/sandbox.c | 4 +- src/common/torerr.c | 222 +++++++++++++++++++++++++++++++++++++++++++++++ src/common/torerr.h | 45 ++++++++++ src/common/torlog.h | 3 - src/common/util.c | 79 ----------------- src/common/util.h | 4 +- src/common/util_bug.h | 15 ---- src/test/test_logging.c | 2 +- 12 files changed, 291 insertions(+), 177 deletions(-)
diff --git a/src/common/backtrace.c b/src/common/backtrace.c index c3c0b77ab..56cefdf93 100644 --- a/src/common/backtrace.c +++ b/src/common/backtrace.c @@ -17,6 +17,7 @@ #include "common/compat.h" #include "common/util.h" #include "common/torlog.h" +#include "common/torerr.h"
#ifdef HAVE_EXECINFO_H #include <execinfo.h> @@ -245,4 +246,3 @@ clean_up_backtrace_handler(void)
tor_free(bt_version); } - diff --git a/src/common/compat_time.c b/src/common/compat_time.c index 5c7067461..913fa666e 100644 --- a/src/common/compat_time.c +++ b/src/common/compat_time.c @@ -34,6 +34,7 @@ #include <mach/mach_time.h> #endif
+#include "common/torerr.h" #include "common/torlog.h" #include "common/util.h" #include "common/container.h" diff --git a/src/common/include.am b/src/common/include.am index 724473c14..67b60d3ee 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -99,6 +99,7 @@ LIBOR_A_SRC = \ src/common/sandbox.c \ src/common/storagedir.c \ src/common/token_bucket.c \ + src/common/torerr.c \ src/common/workqueue.c \ $(libor_extra_source) \ $(threads_impl_source) \ @@ -201,6 +202,7 @@ COMMONHEADERS = \ src/common/testsupport.h \ src/common/timers.h \ src/common/token_bucket.h \ + src/common/torerr.c \ src/common/torint.h \ src/common/torlog.h \ src/common/tortls.h \ diff --git a/src/common/log.c b/src/common/log.c index 6aa2203a5..6dba5bf4b 100644 --- a/src/common/log.c +++ b/src/common/log.c @@ -34,6 +34,8 @@ #define LOG_PRIVATE #include "common/torlog.h" #include "common/container.h" +#include "common/torerr.h" + #ifdef HAVE_ANDROID_LOG_H #include <android/log.h> #endif // HAVE_ANDROID_LOG_H. @@ -265,6 +267,7 @@ void set_log_time_granularity(int granularity_msec) { log_time_granularity = granularity_msec; + tor_log_sigsafe_err_set_granularity(granularity_msec); }
/** Helper: Write the standard prefix for log lines to a @@ -631,71 +634,6 @@ 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[33]; - time_t now = time(NULL); - - if (!m) - return; - if (log_time_granularity >= 2000) { - int g = log_time_granularity / 1000; - now -= now % g; - } - timebuf[0] = now < 0 ? '-' : ' '; - if (now < 0) now = -now; - timebuf[1] = '\0'; - format_dec_number_sigsafe(now, timebuf+1, sizeof(timebuf)-1); - tor_log_err_sigsafe_write("\n==========================================" - "================== T="); - tor_log_err_sigsafe_write(timebuf); - tor_log_err_sigsafe_write("\n"); - tor_log_err_sigsafe_write(m); - va_start(ap, m); - while ((x = va_arg(ap, const char*))) { - tor_log_err_sigsafe_write(x); - } - va_end(ap); -} - -/** Set *<b>out</b> to a pointer to an array of the fds to log errors to from - * inside a signal handler. 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 @@ -717,11 +655,14 @@ tor_log_update_sigsafe_err_fds(void) const logfile_t *lf; int found_real_stderr = 0;
+ int fds[TOR_SIGSAFE_LOG_MAX_FDS]; + int n_fds; + 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; + fds[0] = STDERR_FILENO; + n_fds = 1;
for (lf = logfiles; lf; lf = lf->next) { /* Don't try callback to the control port, or syslogs: We can't @@ -735,22 +676,24 @@ tor_log_update_sigsafe_err_fds(void) if (lf->fd == STDERR_FILENO) found_real_stderr = 1; /* Avoid duplicates */ - if (int_array_contains(sigsafe_log_fds, n_sigsafe_log_fds, lf->fd)) + if (int_array_contains(fds, n_fds, lf->fd)) continue; - sigsafe_log_fds[n_sigsafe_log_fds++] = lf->fd; - if (n_sigsafe_log_fds == MAX_SIGSAFE_FDS) + fds[n_fds++] = lf->fd; + if (n_fds == TOR_SIGSAFE_LOG_MAX_FDS) break; } }
if (!found_real_stderr && - int_array_contains(sigsafe_log_fds, n_sigsafe_log_fds, STDOUT_FILENO)) { + int_array_contains(fds, n_fds, STDOUT_FILENO)) { /* Don't use a virtual stderr when we're also logging to stdout. */ - raw_assert(n_sigsafe_log_fds >= 2); /* Don't tor_assert inside log fns */ - sigsafe_log_fds[0] = sigsafe_log_fds[--n_sigsafe_log_fds]; + raw_assert(n_fds >= 2); /* Don't tor_assert inside log fns */ + fds[0] = fds[--n_fds]; }
UNLOCK_LOGS(); + + tor_log_set_sigsafe_err_fds(fds, n_fds); }
/** Add to <b>out</b> a copy of every currently configured log file name. Used diff --git a/src/common/sandbox.c b/src/common/sandbox.c index cba63e04f..3679037f8 100644 --- a/src/common/sandbox.c +++ b/src/common/sandbox.c @@ -1,4 +1,4 @@ - /* Copyright (c) 2001 Matej Pfajfar. +/* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. * Copyright (c) 2007-2018, The Tor Project, Inc. */ @@ -34,6 +34,7 @@
#include "common/sandbox.h" #include "common/container.h" +#include "common/torerr.h" #include "common/torlog.h" #include "common/torint.h" #include "common/util.h" @@ -1974,4 +1975,3 @@ sandbox_disable_getaddrinfo_cache(void) { } #endif /* !defined(USE_LIBSECCOMP) */ - diff --git a/src/common/torerr.c b/src/common/torerr.c new file mode 100644 index 000000000..c22526254 --- /dev/null +++ b/src/common/torerr.c @@ -0,0 +1,222 @@ +/* Copyright (c) 2001, Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file torerr.c + * + * \brief Handling code for unrecoverable emergencies, at a lower level + * than the logging code. + */ + +#include "orconfig.h" +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#ifdef HAVE_TIME_H +#include <time.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#include "common/torerr.h" + +/** Array of fds to log crash-style warnings to. */ +static int sigsafe_log_fds[TOR_SIGSAFE_LOG_MAX_FDS] = { STDERR_FILENO }; +/** The number of elements used in sigsafe_log_fds */ +static int n_sigsafe_log_fds = 1; +/** Log granularity in milliseconds. */ +static int log_granularity = 1000; + +/** Write <b>s</b> to each element of sigsafe_log_fds. Return 0 on success, -1 + * on failure. */ +static int +tor_log_err_sigsafe_write(const char *s) +{ + int i; + ssize_t r; + size_t len = strlen(s); + int err = 0; + for (i=0; i < n_sigsafe_log_fds; ++i) { + r = write(sigsafe_log_fds[i], s, len); + err += (r != (ssize_t)len); + } + return err ? -1 : 0; +} + +/** Given a list of string arguments ending with a NULL, writes them + * to our logs and to stderr (if possible). This function is safe to call + * from within a signal handler. */ +void +tor_log_err_sigsafe(const char *m, ...) +{ + va_list ap; + const char *x; + char timebuf[33]; + time_t now = time(NULL); + + if (!m) + return; + if (log_granularity >= 2000) { + int g = log_granularity / 1000; + now -= now % g; + } + timebuf[0] = now < 0 ? '-' : ' '; + if (now < 0) now = -now; + timebuf[1] = '\0'; + format_dec_number_sigsafe(now, timebuf+1, sizeof(timebuf)-1); + tor_log_err_sigsafe_write("\n==========================================" + "================== T="); + tor_log_err_sigsafe_write(timebuf); + tor_log_err_sigsafe_write("\n"); + tor_log_err_sigsafe_write(m); + va_start(ap, m); + while ((x = va_arg(ap, const char*))) { + tor_log_err_sigsafe_write(x); + } + va_end(ap); +} + +/** Set *<b>out</b> to a pointer to an array of the fds to log errors to from + * inside a signal handler or other emergency condition. Return the number of + * elements in the array. */ +int +tor_log_get_sigsafe_err_fds(const int **out) +{ + *out = sigsafe_log_fds; + return n_sigsafe_log_fds; +} + +/** + * Update the list of fds that get errors from inside a signal handler or + * other emergency condition. Ignore any beyond the first + * TOR_SIGSAFE_LOG_MAX_FDS. + */ +void +tor_log_set_sigsafe_err_fds(const int *fds, int n) +{ + if (n > TOR_SIGSAFE_LOG_MAX_FDS) { + n = TOR_SIGSAFE_LOG_MAX_FDS; + } + + memcpy(sigsafe_log_fds, fds, n * sizeof(int)); + n_sigsafe_log_fds = n; +} + +/** + * Set the granularity (in ms) to use when reporting fatal errors outside + * the logging system. + */ +void +tor_log_sigsafe_err_set_granularity(int ms) +{ + log_granularity = ms; +} + +/** + * Log an emergency assertion failure message. + * + * This kind of message is safe to send from within a log handler, + * a signal handler, or other emergency situation. + */ +void +tor_raw_assertion_failed_msg_(const char *file, int line, const char *expr, + const char *msg) +{ + char linebuf[16]; + format_dec_number_sigsafe(line, linebuf, sizeof(linebuf)); + tor_log_err_sigsafe("INTERNAL ERROR: Raw assertion failed at ", + file, ":", linebuf, ": ", expr, NULL); + if (msg) { + tor_log_err_sigsafe_write(msg); + tor_log_err_sigsafe_write("\n"); + } +} + +/* 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 = (unsigned) (x % radix); + raw_assert(cp > buf); + --cp; + *cp = "0123456789ABCDEF"[digit]; + x /= radix; + } while (x); + + /* NOT tor_assert; see above. */ + if (cp != buf) { + abort(); // LCOV_EXCL_LINE + } + + return len; +} + +/** + * Helper function to output hex numbers from within a signal handler. + * + * Writes the nul-terminated hexadecimal digits of <b>x</b> into a buffer + * <b>buf</b> of size <b>buf_len</b>, and return the actual number of digits + * written, not counting the terminal NUL. + * + * If there is insufficient space, write nothing and return 0. + * + * This accepts an unsigned int because format_helper_exit_status() needs to + * call it with a signed int and an unsigned char, and since the C standard + * does not guarantee that an int is wider than a char (an int must be at + * least 16 bits but it is permitted for a char to be that wide as well), we + * can't assume a signed int is sufficient to accommodate an unsigned char. + * Thus, format_helper_exit_status() will still need to emit any require '-' + * on its own. + * + * For most purposes, you'd want to use tor_snprintf("%x") instead of this + * function; it's designed to be used in code paths where you can't call + * arbitrary C functions. + */ +int +format_hex_number_sigsafe(unsigned long x, char *buf, int buf_len) +{ + return format_number_sigsafe(x, buf, buf_len, 16); +} + +/** As format_hex_number_sigsafe, but format the number in base 10. */ +int +format_dec_number_sigsafe(unsigned long x, char *buf, int buf_len) +{ + return format_number_sigsafe(x, buf, buf_len, 10); +} diff --git a/src/common/torerr.h b/src/common/torerr.h new file mode 100644 index 000000000..09d8eeb76 --- /dev/null +++ b/src/common/torerr.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2001, Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file torerr.h + * + * \brief Headers for torerr.c. + **/ + +#ifndef TOR_TORERR_H +#define TOR_TORERR_H + +/* The raw_assert...() variants are for use within code that can't call + * tor_assertion_failed_() because of call circularity issues. */ +#define raw_assert(expr) do { \ + if (!(expr)) { \ + tor_raw_assertion_failed_msg_(__FILE__, __LINE__, #expr, NULL); \ + abort(); \ + } \ + } while (0) +#define raw_assert_unreached(expr) raw_assert(0) +#define raw_assert_unreached_msg(msg) do { \ + tor_raw_assertion_failed_msg_(__FILE__, __LINE__, "0", (msg)); \ + abort(); \ + } while (0) + +void tor_raw_assertion_failed_msg_(const char *file, int line, + const char *expr, + const char *msg); + +/** Maximum number of fds that will get notifications if we crash */ +#define TOR_SIGSAFE_LOG_MAX_FDS 8 + +void tor_log_err_sigsafe(const char *m, ...); +int tor_log_get_sigsafe_err_fds(const int **out); +void tor_log_set_sigsafe_err_fds(const int *fds, int n); +void tor_log_sigsafe_err_set_granularity(int ms); + +int format_hex_number_sigsafe(unsigned long x, char *buf, int max_len); +int format_dec_number_sigsafe(unsigned long x, char *buf, int max_len); + +#endif /* !defined(TOR_TORLOG_H) */ diff --git a/src/common/torlog.h b/src/common/torlog.h index 65921c7b0..56f922d40 100644 --- a/src/common/torlog.h +++ b/src/common/torlog.h @@ -175,8 +175,6 @@ void truncate_logs(void); 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);
struct smartlist_t; @@ -272,4 +270,3 @@ MOCK_DECL(STATIC void, logv, (int severity, log_domain_mask_t domain,
# define TOR_TORLOG_H #endif /* !defined(TOR_TORLOG_H) */ - diff --git a/src/common/util.c b/src/common/util.c index 7d10a9e24..5a477eeba 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -3865,85 +3865,6 @@ 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 = (unsigned) (x % radix); - tor_assert(cp > buf); - --cp; - *cp = "0123456789ABCDEF"[digit]; - x /= radix; - } while (x); - - /* NOT tor_assert; see above. */ - if (cp != buf) { - abort(); // LCOV_EXCL_LINE - } - - return len; -} - -/** - * Helper function to output hex numbers from within a signal handler. - * - * Writes the nul-terminated hexadecimal digits of <b>x</b> into a buffer - * <b>buf</b> of size <b>buf_len</b>, and return the actual number of digits - * written, not counting the terminal NUL. - * - * If there is insufficient space, write nothing and return 0. - * - * This accepts an unsigned int because format_helper_exit_status() needs to - * call it with a signed int and an unsigned char, and since the C standard - * does not guarantee that an int is wider than a char (an int must be at - * least 16 bits but it is permitted for a char to be that wide as well), we - * can't assume a signed int is sufficient to accommodate an unsigned char. - * Thus, format_helper_exit_status() will still need to emit any require '-' - * on its own. - * - * For most purposes, you'd want to use tor_snprintf("%x") instead of this - * function; it's designed to be used in code paths where you can't call - * arbitrary C functions. - */ -int -format_hex_number_sigsafe(unsigned long x, char *buf, int buf_len) -{ - return format_number_sigsafe(x, buf, buf_len, 16); -} - -/** As format_hex_number_sigsafe, but format the number in base 10. */ -int -format_dec_number_sigsafe(unsigned long x, char *buf, int buf_len) -{ - return format_number_sigsafe(x, buf, buf_len, 10); -} - #ifndef _WIN32 /** Format <b>child_state</b> and <b>saved_errno</b> as a hex string placed in * <b>hex_errno</b>. Called between fork and _exit, so must be signal-handler diff --git a/src/common/util.h b/src/common/util.h index 7921357ac..a5e6c4d46 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -22,6 +22,7 @@ /* for the correct alias to struct stat */ #include <sys/stat.h> #endif +#include "common/torerr.h" #include "common/util_bug.h"
#ifndef O_BINARY @@ -522,9 +523,6 @@ 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 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/common/util_bug.h b/src/common/util_bug.h index 0e1af2da1..1d499a197 100644 --- a/src/common/util_bug.h +++ b/src/common/util_bug.h @@ -55,21 +55,6 @@ #error "Sorry; we don't support building with NDEBUG." #endif /* defined(NDEBUG) */
-/* The raw_assert...() variants are for use within code that can't call - * tor_assertion_failed_() because of call circularity issues. */ -#define raw_assert(expr) STMT_BEGIN \ - if (!(expr)) { \ - fprintf(stderr, "RAW ASSERTION FAILURE AT %s:%d: %s\n", \ - __FILE__, __LINE__, #expr); \ - abort(); \ - } \ - STMT_END -#define raw_assert_unreached(expr) raw_assert(0) -#define raw_assert_unreached_msg(msg) STMT_BEGIN \ - fprintf(stderr, "ERROR: %s\n", (msg)); \ - raw_assert_unreached(); \ - STMT_END - /* Sometimes we don't want to use assertions during branch coverage tests; it * leads to tons of unreached branches which in reality are only assertions we * didn't hit. */ diff --git a/src/test/test_logging.c b/src/test/test_logging.c index aebfd3606..eaad7ed13 100644 --- a/src/test/test_logging.c +++ b/src/test/test_logging.c @@ -3,6 +3,7 @@
#include "orconfig.h" #include "or/or.h" +#include "common/torerr.h" #include "common/torlog.h" #include "test/test.h"
@@ -170,4 +171,3 @@ struct testcase_t logging_tests[] = { { "ratelim", test_ratelim, 0, NULL, NULL }, END_OF_TESTCASES }; -
tor-commits@lists.torproject.org