mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-10 05:03:43 +01:00
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:
commit
fbc20294aa
2
.gitignore
vendored
2
.gitignore
vendored
@ -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
33
LICENSE
@ -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
8
changes/stack_trace
Normal 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.
|
||||
|
@ -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
131
m4/pc_from_ucontext.m4
Normal 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
202
src/common/backtrace.c
Normal 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
12
src/common/backtrace.h
Normal 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
|
||||
|
@ -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 \
|
||||
|
140
src/common/log.c
140
src/common/log.c
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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_;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) */
|
||||
|
@ -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) {
|
||||
|
@ -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
42
src/test/bt_test.py
Executable 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"
|
||||
|
@ -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
|
||||
|
@ -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
110
src/test/test_bt_cl.c
Normal 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
135
src/test/test_logging.c
Normal 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
|
||||
};
|
||||
|
@ -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),
|
||||
|
Loading…
Reference in New Issue
Block a user