Line data Source code
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 :
22 : #include <string.h>
23 : #include <stdlib.h>
24 :
25 : #ifdef TOR_UNIT_TESTS
26 : static void (*failed_assertion_cb)(void) = NULL;
27 : static int n_bugs_to_capture = 0;
28 : static smartlist_t *bug_messages = NULL;
29 : #define capturing_bugs() (bug_messages != NULL && n_bugs_to_capture)
30 : void
31 41 : tor_capture_bugs_(int n)
32 : {
33 41 : tor_end_capture_bugs_();
34 41 : bug_messages = smartlist_new();
35 41 : n_bugs_to_capture = n;
36 41 : }
37 : void
38 94 : tor_end_capture_bugs_(void)
39 : {
40 94 : n_bugs_to_capture = 0;
41 94 : if (!bug_messages)
42 : return;
43 73 : SMARTLIST_FOREACH(bug_messages, char *, cp, tor_free(cp));
44 41 : smartlist_free(bug_messages);
45 41 : bug_messages = NULL;
46 : }
47 : const smartlist_t *
48 65 : tor_get_captured_bug_log_(void)
49 : {
50 65 : return bug_messages;
51 : }
52 : static void
53 32 : add_captured_bug(const char *s)
54 : {
55 32 : --n_bugs_to_capture;
56 32 : smartlist_add_strdup(bug_messages, s);
57 32 : }
58 : /** Set a callback to be invoked when we get any tor_bug_occurred_
59 : * invocation. We use this in the unit tests so that a nonfatal
60 : * assertion failure can also count as a test failure.
61 : */
62 : void
63 5318 : tor_set_failed_assertion_callback(void (*fn)(void))
64 : {
65 5318 : failed_assertion_cb = fn;
66 5318 : }
67 : #else /* !defined(TOR_UNIT_TESTS) */
68 : #define capturing_bugs() (0)
69 : #define add_captured_bug(s) do { } while (0)
70 : #endif /* defined(TOR_UNIT_TESTS) */
71 :
72 : /** Helper for tor_assert: report the assertion failure. */
73 : void
74 0 : tor_assertion_failed_(const char *fname, unsigned int line,
75 : const char *func, const char *expr,
76 : const char *fmt, ...)
77 : {
78 0 : char *buf = NULL;
79 0 : char *extra = NULL;
80 0 : va_list ap;
81 :
82 : #ifdef __clang__
83 : #pragma clang diagnostic push
84 : #pragma clang diagnostic ignored "-Wformat-nonliteral"
85 : #endif
86 0 : if (fmt) {
87 0 : va_start(ap,fmt);
88 0 : tor_vasprintf(&extra, fmt, ap);
89 0 : va_end(ap);
90 : }
91 : #ifdef __clang__
92 : #pragma clang diagnostic pop
93 : #endif
94 :
95 0 : log_err(LD_BUG, "%s:%u: %s: Assertion %s failed; aborting.",
96 : fname, line, func, expr);
97 0 : tor_asprintf(&buf, "Assertion %s failed in %s at %s:%u: %s",
98 0 : expr, func, fname, line, extra ? extra : "");
99 0 : tor_free(extra);
100 0 : log_backtrace(LOG_ERR, LD_BUG, buf);
101 0 : tor_free(buf);
102 0 : }
103 :
104 : /** Helper for tor_assert_nonfatal: report the assertion failure. */
105 : void
106 32 : tor_bug_occurred_(const char *fname, unsigned int line,
107 : const char *func, const char *expr,
108 : int once, const char *fmt, ...)
109 : {
110 32 : char *buf = NULL;
111 64 : const char *once_str = once ?
112 32 : " (Future instances of this warning will be silenced.)": "";
113 32 : if (! expr) {
114 1 : if (capturing_bugs()) {
115 1 : add_captured_bug("This line should not have been reached.");
116 33 : return;
117 : }
118 0 : log_warn(LD_BUG, "%s:%u: %s: This line should not have been reached.%s",
119 : fname, line, func, once_str);
120 0 : tor_asprintf(&buf,
121 : "Line unexpectedly reached at %s at %s:%u",
122 : func, fname, line);
123 : } else {
124 31 : if (capturing_bugs()) {
125 31 : add_captured_bug(expr);
126 31 : return;
127 : }
128 :
129 0 : va_list ap;
130 0 : char *extra = NULL;
131 :
132 : #ifdef __clang__
133 : #pragma clang diagnostic push
134 : #pragma clang diagnostic ignored "-Wformat-nonliteral"
135 : #endif
136 0 : if (fmt) {
137 0 : va_start(ap,fmt);
138 0 : tor_vasprintf(&extra, fmt, ap);
139 0 : va_end(ap);
140 : }
141 : #ifdef __clang__
142 : #pragma clang diagnostic pop
143 : #endif
144 :
145 0 : log_warn(LD_BUG, "%s:%u: %s: Non-fatal assertion %s failed.%s",
146 : fname, line, func, expr, once_str);
147 0 : tor_asprintf(&buf, "Non-fatal assertion %s failed in %s at %s:%u%s%s",
148 : expr, func, fname, line, fmt ? " : " : "",
149 0 : extra ? extra : "");
150 0 : tor_free(extra);
151 : }
152 0 : log_backtrace(LOG_WARN, LD_BUG, buf);
153 0 : tor_free(buf);
154 :
155 : #ifdef TOR_UNIT_TESTS
156 0 : if (failed_assertion_cb) {
157 0 : failed_assertion_cb();
158 : }
159 : #endif
160 : }
161 :
162 : /**
163 : * Call the tor_raw_abort_() function to close raw logs, then kill the current
164 : * process with a fatal error. But first, close the file-based log file
165 : * descriptors, so error messages are written before process termination.
166 : *
167 : * (This is a separate function so that we declare it in util_bug.h without
168 : * including torerr.h in all the users of util_bug.h)
169 : **/
170 : void
171 0 : tor_abort_(void)
172 : {
173 0 : logs_flush_sigsafe();
174 0 : tor_raw_abort_();
175 : }
176 :
177 : #ifdef _WIN32
178 : /** Take a filename and return a pointer to its final element. This
179 : * function is called on __FILE__ to fix a MSVC nit where __FILE__
180 : * contains the full path to the file. This is bad, because it
181 : * confuses users to find the home directory of the person who
182 : * compiled the binary in their warning messages.
183 : */
184 : const char *
185 : tor_fix_source_file(const char *fname)
186 : {
187 : const char *cp1, *cp2, *r;
188 : cp1 = strrchr(fname, '/');
189 : cp2 = strrchr(fname, '\\');
190 : if (cp1 && cp2) {
191 : r = (cp1<cp2)?(cp2+1):(cp1+1);
192 : } else if (cp1) {
193 : r = cp1+1;
194 : } else if (cp2) {
195 : r = cp2+1;
196 : } else {
197 : r = fname;
198 : }
199 : return r;
200 : }
201 : #endif /* defined(_WIN32) */
|