tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

util_bug.c (5853B)


      1 /* Copyright (c) 2003, Roger Dingledine
      2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
      3 * Copyright (c) 2007-2021, The Tor Project, Inc. */
      4 /* See LICENSE for licensing information */
      5 
      6 /**
      7 * \file util_bug.c
      8 **/
      9 
     10 #include "orconfig.h"
     11 #include "lib/log/util_bug.h"
     12 #include "lib/log/log.h"
     13 #include "lib/err/backtrace.h"
     14 #include "lib/err/torerr.h"
     15 #ifdef TOR_UNIT_TESTS
     16 #include "lib/smartlist_core/smartlist_core.h"
     17 #include "lib/smartlist_core/smartlist_foreach.h"
     18 #endif
     19 #include "lib/malloc/malloc.h"
     20 #include "lib/string/printf.h"
     21 #include "lib/thread/threads.h"
     22 
     23 #include <string.h>
     24 #include <stdlib.h>
     25 
     26 #ifdef TOR_UNIT_TESTS
     27 static void (*failed_assertion_cb)(void) = NULL;
     28 static int n_bugs_to_capture = 0;
     29 static smartlist_t *bug_messages = NULL;
     30 #define capturing_bugs() (bug_messages != NULL && n_bugs_to_capture)
     31 void
     32 tor_capture_bugs_(int n)
     33 {
     34  tor_end_capture_bugs_();
     35  bug_messages = smartlist_new();
     36  n_bugs_to_capture = n;
     37 }
     38 void
     39 tor_end_capture_bugs_(void)
     40 {
     41  n_bugs_to_capture = 0;
     42  if (!bug_messages)
     43    return;
     44  SMARTLIST_FOREACH(bug_messages, char *, cp, tor_free(cp));
     45  smartlist_free(bug_messages);
     46  bug_messages = NULL;
     47 }
     48 const smartlist_t *
     49 tor_get_captured_bug_log_(void)
     50 {
     51  return bug_messages;
     52 }
     53 static void
     54 add_captured_bug(const char *s)
     55 {
     56  --n_bugs_to_capture;
     57  smartlist_add_strdup(bug_messages, s);
     58 }
     59 /** Set a callback to be invoked when we get any tor_bug_occurred_
     60 * invocation. We use this in the unit tests so that a nonfatal
     61 * assertion failure can also count as a test failure.
     62 */
     63 void
     64 tor_set_failed_assertion_callback(void (*fn)(void))
     65 {
     66  failed_assertion_cb = fn;
     67 }
     68 #else /* !defined(TOR_UNIT_TESTS) */
     69 #define capturing_bugs() (0)
     70 #define add_captured_bug(s) do { } while (0)
     71 #endif /* defined(TOR_UNIT_TESTS) */
     72 
     73 /** Helper for tor_assert: report the assertion failure. */
     74 void
     75 tor_assertion_failed_(const char *fname, unsigned int line,
     76                      const char *func, const char *expr,
     77                      const char *fmt, ...)
     78 {
     79  char *buf = NULL;
     80  char *extra = NULL;
     81  va_list ap;
     82 
     83 #ifdef __clang__
     84 #pragma clang diagnostic push
     85 #pragma clang diagnostic ignored "-Wformat-nonliteral"
     86 #endif
     87  if (fmt) {
     88    va_start(ap,fmt);
     89    tor_vasprintf(&extra, fmt, ap);
     90    va_end(ap);
     91  }
     92 #ifdef __clang__
     93 #pragma clang diagnostic pop
     94 #endif
     95 
     96  log_err(LD_BUG, "%s:%u: %s: Assertion %s failed; aborting.",
     97          fname, line, func, expr);
     98  tor_asprintf(&buf, "Assertion %s failed in %s at %s:%u: %s",
     99               expr, func, fname, line, extra ? extra : "");
    100  tor_free(extra);
    101  log_backtrace(LOG_ERR, LD_BUG, buf);
    102  tor_free(buf);
    103 }
    104 
    105 static atomic_counter_t total_bug_reached;
    106 
    107 void
    108 tor_bug_init_counter(void)
    109 {
    110  atomic_counter_init(&total_bug_reached);
    111 }
    112 
    113 /** Helper to update BUG count in metrics. */
    114 void
    115 tor_bug_increment_count_(void)
    116 {
    117  atomic_counter_add(&total_bug_reached, 1);
    118 }
    119 
    120 size_t
    121 tor_bug_get_count(void)
    122 {
    123  return atomic_counter_get(&total_bug_reached);
    124 }
    125 
    126 /** Helper for tor_assert_nonfatal: report the assertion failure. */
    127 void
    128 tor_bug_occurred_(const char *fname, unsigned int line,
    129                  const char *func, const char *expr,
    130                  int once, const char *fmt, ...)
    131 {
    132  char *buf = NULL;
    133  const char *once_str = once ?
    134    " (Future instances of this warning will be silenced.)": "";
    135  if (! once) {
    136    // _once assertions count from the macro directly so we count them as many
    137    // times as they are reached, and not just once.
    138    tor_bug_increment_count_();
    139  }
    140  if (! expr) {
    141    if (capturing_bugs()) {
    142      add_captured_bug("This line should not have been reached.");
    143      return;
    144    }
    145    log_warn(LD_BUG, "%s:%u: %s: This line should not have been reached.%s",
    146             fname, line, func, once_str);
    147    tor_asprintf(&buf,
    148                 "Line unexpectedly reached at %s at %s:%u",
    149                 func, fname, line);
    150  } else {
    151    if (capturing_bugs()) {
    152      add_captured_bug(expr);
    153      return;
    154    }
    155 
    156    va_list ap;
    157    char *extra = NULL;
    158 
    159 #ifdef __clang__
    160 #pragma clang diagnostic push
    161 #pragma clang diagnostic ignored "-Wformat-nonliteral"
    162 #endif
    163    if (fmt) {
    164      va_start(ap,fmt);
    165      tor_vasprintf(&extra, fmt, ap);
    166      va_end(ap);
    167    }
    168 #ifdef __clang__
    169 #pragma clang diagnostic pop
    170 #endif
    171 
    172    log_warn(LD_BUG, "%s:%u: %s: Non-fatal assertion %s failed.%s",
    173             fname, line, func, expr, once_str);
    174    tor_asprintf(&buf, "Non-fatal assertion %s failed in %s at %s:%u%s%s",
    175                 expr, func, fname, line, fmt ? " : " : "",
    176                 extra ? extra : "");
    177    tor_free(extra);
    178  }
    179  log_backtrace(LOG_WARN, LD_BUG, buf);
    180  tor_free(buf);
    181 
    182 #ifdef TOR_UNIT_TESTS
    183  if (failed_assertion_cb) {
    184    failed_assertion_cb();
    185  }
    186 #endif
    187 }
    188 
    189 /**
    190 * Call the tor_raw_abort_() function to close raw logs, then kill the current
    191 * process with a fatal error. But first, close the file-based log file
    192 * descriptors, so error messages are written before process termination.
    193 *
    194 * (This is a separate function so that we declare it in util_bug.h without
    195 * including torerr.h in all the users of util_bug.h)
    196 **/
    197 void
    198 tor_abort_(void)
    199 {
    200  logs_flush_sigsafe();
    201  tor_raw_abort_();
    202 }
    203 
    204 #ifdef _WIN32
    205 /** Take a filename and return a pointer to its final element.  This
    206 * function is called on __FILE__ to fix a MSVC nit where __FILE__
    207 * contains the full path to the file.  This is bad, because it
    208 * confuses users to find the home directory of the person who
    209 * compiled the binary in their warning messages.
    210 */
    211 const char *
    212 tor_fix_source_file(const char *fname)
    213 {
    214  const char *cp1, *cp2, *r;
    215  cp1 = strrchr(fname, '/');
    216  cp2 = strrchr(fname, '\\');
    217  if (cp1 && cp2) {
    218    r = (cp1<cp2)?(cp2+1):(cp1+1);
    219  } else if (cp1) {
    220    r = cp1+1;
    221  } else if (cp2) {
    222    r = cp2+1;
    223  } else {
    224    r = fname;
    225  }
    226  return r;
    227 }
    228 #endif /* defined(_WIN32) */