Merge branch 'backtrace_squashed'

Conflicts:
	src/common/sandbox.c
	src/common/sandbox.h
	src/common/util.c
	src/or/main.c
	src/test/include.am
	src/test/test.c
This commit is contained in:
Nick Mathewson 2013-11-18 11:00:16 -05:00
commit fbc20294aa
22 changed files with 977 additions and 114 deletions

2
.gitignore vendored
View File

@ -162,9 +162,11 @@
/src/test/bench
/src/test/bench.exe
/src/test/test
/src/test/test-bt-cl
/src/test/test-child
/src/test/test-ntor-cl
/src/test/test.exe
/src/test/test-bt-cl.exe
/src/test/test-child.exe
/src/test/test-ntor-cl.exe

33
LICENSE
View File

@ -134,6 +134,39 @@ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
DATABASE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
===============================================================================
m4/pc_from_ucontext.m4 is available under the following license. Note that
it is *not* built into the Tor license.
Copyright (c) 2005, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
===============================================================================
If you got Tor as a static binary with OpenSSL included, then you should know:
"This product includes software developed by the OpenSSL Project

8
changes/stack_trace Normal file
View File

@ -0,0 +1,8 @@
o Major features:
- On some platforms (currently: recent OSX versions, glibc-based
platforms that support the ELF format, and a few other
Unix-like operating systems), Tor can now dump stack traces
when a crash occurs or an assertion fails. By default, traces
are dumped to stderr (if possible) and to any logs that are
reporting errors.

View File

@ -326,6 +326,8 @@ dnl exports strlcpy without defining it in a header.
AC_CHECK_FUNCS(
_NSGetEnviron \
accept4 \
backtrace \
backtrace_symbols_fd \
clock_gettime \
flock \
ftime \
@ -343,6 +345,7 @@ AC_CHECK_FUNCS(
memmem \
prctl \
rint \
sigaction \
socketpair \
strlcat \
strlcpy \
@ -629,6 +632,9 @@ if test x$enable_linker_hardening != xno; then
TOR_CHECK_LDFLAGS(-z relro -z now, "$all_ldflags_for_check", "$all_libs_for_check")
fi
# For backtrace support
TOR_CHECK_LDFLAGS(-rdynamic)
dnl ------------------------------------------------------
dnl Now see if we have a -fomit-frame-pointer compiler option.
@ -841,6 +847,7 @@ dnl These headers are not essential
AC_CHECK_HEADERS(
arpa/inet.h \
crt_externs.h \
execinfo.h \
grp.h \
ifaddrs.h \
inttypes.h \
@ -969,6 +976,8 @@ AC_CHECK_SIZEOF(pid_t)
AC_CHECK_TYPES([uint, u_char, ssize_t])
AC_PC_FROM_UCONTEXT([/bin/true])
dnl used to include sockaddr_storage, but everybody has that.
AC_CHECK_TYPES([struct in6_addr, struct sockaddr_in6, sa_family_t], , ,
[#ifdef HAVE_SYS_TYPES_H

131
m4/pc_from_ucontext.m4 Normal file
View File

@ -0,0 +1,131 @@
# This file is from Google Performance Tools, svn revision r226.
#
# The Google Performance Tools license is:
########
# Copyright (c) 2005, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
########
# Original file follows below.
# We want to access the "PC" (Program Counter) register from a struct
# ucontext. Every system has its own way of doing that. We try all the
# possibilities we know about. Note REG_PC should come first (REG_RIP
# is also defined on solaris, but does the wrong thing).
# OpenBSD doesn't have ucontext.h, but we can get PC from ucontext_t
# by using signal.h.
# The first argument of AC_PC_FROM_UCONTEXT will be invoked when we
# cannot find a way to obtain PC from ucontext.
AC_DEFUN([AC_PC_FROM_UCONTEXT],
[AC_CHECK_HEADERS(ucontext.h)
# Redhat 7 has <sys/ucontext.h>, but it barfs if we #include it directly
# (this was fixed in later redhats). <ucontext.h> works fine, so use that.
if grep "Red Hat Linux release 7" /etc/redhat-release >/dev/null 2>&1; then
AC_DEFINE(HAVE_SYS_UCONTEXT_H, 0, [<sys/ucontext.h> is broken on redhat 7])
ac_cv_header_sys_ucontext_h=no
else
AC_CHECK_HEADERS(sys/ucontext.h) # ucontext on OS X 10.6 (at least)
fi
AC_CHECK_HEADERS(cygwin/signal.h) # ucontext on cywgin
AC_MSG_CHECKING([how to access the program counter from a struct ucontext])
pc_fields=" uc_mcontext.gregs[[REG_PC]]" # Solaris x86 (32 + 64 bit)
pc_fields="$pc_fields uc_mcontext.gregs[[REG_EIP]]" # Linux (i386)
pc_fields="$pc_fields uc_mcontext.gregs[[REG_RIP]]" # Linux (x86_64)
pc_fields="$pc_fields uc_mcontext.sc_ip" # Linux (ia64)
pc_fields="$pc_fields uc_mcontext.uc_regs->gregs[[PT_NIP]]" # Linux (ppc)
pc_fields="$pc_fields uc_mcontext.gregs[[R15]]" # Linux (arm old [untested])
pc_fields="$pc_fields uc_mcontext.arm_pc" # Linux (arm arch 5)
pc_fields="$pc_fields uc_mcontext.gp_regs[[PT_NIP]]" # Suse SLES 11 (ppc64)
pc_fields="$pc_fields uc_mcontext.mc_eip" # FreeBSD (i386)
pc_fields="$pc_fields uc_mcontext.mc_rip" # FreeBSD (x86_64 [untested])
pc_fields="$pc_fields uc_mcontext.__gregs[[_REG_EIP]]" # NetBSD (i386)
pc_fields="$pc_fields uc_mcontext.__gregs[[_REG_RIP]]" # NetBSD (x86_64)
pc_fields="$pc_fields uc_mcontext->ss.eip" # OS X (i386, <=10.4)
pc_fields="$pc_fields uc_mcontext->__ss.__eip" # OS X (i386, >=10.5)
pc_fields="$pc_fields uc_mcontext->ss.rip" # OS X (x86_64)
pc_fields="$pc_fields uc_mcontext->__ss.__rip" # OS X (>=10.5 [untested])
pc_fields="$pc_fields uc_mcontext->ss.srr0" # OS X (ppc, ppc64 [untested])
pc_fields="$pc_fields uc_mcontext->__ss.__srr0" # OS X (>=10.5 [untested])
pc_field_found=false
for pc_field in $pc_fields; do
if ! $pc_field_found; then
# Prefer sys/ucontext.h to ucontext.h, for OS X's sake.
if test "x$ac_cv_header_cygwin_signal_h" = xyes; then
AC_TRY_COMPILE([#define _GNU_SOURCE 1
#include <cygwin/signal.h>],
[ucontext_t u; return u.$pc_field == 0;],
AC_DEFINE_UNQUOTED(PC_FROM_UCONTEXT, $pc_field,
How to access the PC from a struct ucontext)
AC_MSG_RESULT([$pc_field])
pc_field_found=true)
elif test "x$ac_cv_header_sys_ucontext_h" = xyes; then
AC_TRY_COMPILE([#define _GNU_SOURCE 1
#include <sys/ucontext.h>],
[ucontext_t u; return u.$pc_field == 0;],
AC_DEFINE_UNQUOTED(PC_FROM_UCONTEXT, $pc_field,
How to access the PC from a struct ucontext)
AC_MSG_RESULT([$pc_field])
pc_field_found=true)
elif test "x$ac_cv_header_ucontext_h" = xyes; then
AC_TRY_COMPILE([#define _GNU_SOURCE 1
#include <ucontext.h>],
[ucontext_t u; return u.$pc_field == 0;],
AC_DEFINE_UNQUOTED(PC_FROM_UCONTEXT, $pc_field,
How to access the PC from a struct ucontext)
AC_MSG_RESULT([$pc_field])
pc_field_found=true)
else # hope some standard header gives it to us
AC_TRY_COMPILE([],
[ucontext_t u; return u.$pc_field == 0;],
AC_DEFINE_UNQUOTED(PC_FROM_UCONTEXT, $pc_field,
How to access the PC from a struct ucontext)
AC_MSG_RESULT([$pc_field])
pc_field_found=true)
fi
fi
done
if ! $pc_field_found; then
pc_fields=" sc_eip" # OpenBSD (i386)
pc_fields="$pc_fields sc_rip" # OpenBSD (x86_64)
for pc_field in $pc_fields; do
if ! $pc_field_found; then
AC_TRY_COMPILE([#include <signal.h>],
[ucontext_t u; return u.$pc_field == 0;],
AC_DEFINE_UNQUOTED(PC_FROM_UCONTEXT, $pc_field,
How to access the PC from a struct ucontext)
AC_MSG_RESULT([$pc_field])
pc_field_found=true)
fi
done
fi
if ! $pc_field_found; then
[$1]
fi])

202
src/common/backtrace.c Normal file
View File

@ -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);
}

12
src/common/backtrace.h Normal file
View File

@ -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

View File

@ -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 \

View File

@ -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)

View File

@ -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,

View File

@ -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.

View File

@ -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_;

View File

@ -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>
@ -94,6 +95,23 @@
#include <sys/wait.h>
#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
* ===== */
@ -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;
return format_number_sigsafe(x, buf, buf_len, 16);
}
/* 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 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;
}

View File

@ -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) */

View File

@ -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) {

View File

@ -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();

42
src/test/bt_test.py Executable file
View File

@ -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"

View File

@ -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

View File

@ -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 },

110
src/test/test_bt_cl.c Normal file
View File

@ -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;
}

135
src/test/test_logging.c Normal file
View File

@ -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
};

View File

@ -2778,6 +2778,56 @@ test_util_format_hex_number(void *ptr)
return;
}
/**
* 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
*/
@ -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),