aboutsummaryrefslogtreecommitdiff
path: root/src/lib/err/torerr.c
blob: 2de75c0be43198e7e2432f152227415fce8857a2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
/* Copyright (c) 2001, Matej Pfajfar.
 * Copyright (c) 2001-2004, Roger Dingledine.
 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
 * Copyright (c) 2007-2020, 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.
 *
 * There are plenty of places that things can go wrong in Tor's backend
 * libraries: the allocator can fail, the locking subsystem can fail, and so
 * on.  But since these subsystems are used themselves by the logging module,
 * they can't use the logging code directly to report their errors.
 *
 * As a workaround, the logging code provides this module with a set of raw
 * fds to be used for reporting errors in the lowest-level Tor code.
 */

#include "orconfig.h"
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.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 "lib/err/torerr.h"
#include "lib/err/backtrace.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.
 *
 * These fds must remain open even after the log module has shut down. (And
 * they should remain open even while logs are being reconfigured.) Therefore,
 * any fds closed by the log module should be dup()ed, and the duplicate fd
 * should be given to the err module in fds. In particular, the log module
 * closes the file log fds, but does not close the stdio log fds.
 *
 * If fds is NULL or n is 0, clears the list of error 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;
  }

  /* Clear the entire array. This code mitigates against some race conditions,
   * but there are still some races here:
   * - err logs are disabled while the array is cleared, and
   * - a thread can read the old value of n_sigsafe_log_fds, then read a
   *   partially written array.
   * We could fix these races using atomics, but atomics use the err module. */
  n_sigsafe_log_fds = 0;
  memset(sigsafe_log_fds, 0, sizeof(sigsafe_log_fds));
  if (fds && n > 0) {
    memcpy(sigsafe_log_fds, fds, n * sizeof(int));
    n_sigsafe_log_fds = n;
  }
}

/**
 * Reset the list of emergency error fds to its default.
 */
void
tor_log_reset_sigsafe_err_fds(void)
{
  int fds[] = { STDERR_FILENO };
  tor_log_set_sigsafe_err_fds(fds, 1);
}

/**
 * Flush the list of fds that get errors from inside a signal handler or
 * other emergency condition. These fds are shared with the logging code:
 * flushing them also flushes the log buffers.
 *
 * This function is safe to call during signal handlers.
 */
void
tor_log_flush_sigsafe_err_fds(void)
{
  /* If we don't have fsync() in unistd.h, we can't flush the logs. */
#ifdef HAVE_FSYNC
  int n_fds, i;
  const int *fds = NULL;

  n_fds = tor_log_get_sigsafe_err_fds(&fds);
  for (i = 0; i < n_fds; ++i) {
    /* This function is called on error and on shutdown, so we don't log, or
     * take any other action, if fsync() fails. */
    (void)fsync(fds[i]);
  }
#endif /* defined(HAVE_FSYNC) */
}

/**
 * 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 in ",
                      get_tor_backtrace_version(), " at ",
                      file, ":", linebuf, ": ", expr, "\n", NULL);
  if (msg) {
    tor_log_err_sigsafe_write(msg);
    tor_log_err_sigsafe_write("\n");
  }

  dump_stack_symbols_to_error_fds();

  /* Some platforms (macOS, maybe others?) can swallow the last write before an
   * abort. This issue is probably caused by a race condition between write
   * buffer cache flushing, and process termination. So we write an extra
   * newline, to make sure that the message always gets through. */
  tor_log_err_sigsafe_write("\n");
}

/**
 * Call the abort() function to kill the current process with a fatal
 * error. But first, flush the raw error file descriptors, so error messages
 * are written before process termination.
 **/
void
tor_raw_abort_(void)
{
  tor_log_flush_sigsafe_err_fds();
  abort();
}

/* 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.  Not even
   * raw_assert(), since raw_assert() calls this function on failure. */
  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);
    if (cp <= buf) {
      /* Not tor_assert(); see above. */
      tor_raw_abort_();
    }
    --cp;
    *cp = "0123456789ABCDEF"[digit];
    x /= radix;
  } while (x);

  /* NOT tor_assert; see above. */
  if (cp != buf) {
    tor_raw_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, callers will still need to add any required '-' to the final string.
 *
 * 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);
}