ICONV: OFF")
endif ()
+if (HAVE_LIBUNWIND)
+ message("\
+ Libunwind: ON")
+else ()
+ message("\
+ Libunwind: OFF")
+endif ()
+
if (HAVE_LZMA)
message("\
LZMA: ON")
--- /dev/null
+# FindLibunwind
+# ---------
+#
+# Find the libunwind includes and library.
+#
+# Result Variables
+# ^^^^^^^^^^^^^^^^
+#
+# This module will set the following variables in your project:
+#
+# ``LIBUNWIND_INCLUDE_DIRS``
+# where to find rpc.h, etc.
+# ``LIBUNWIND_LIBRARIES``
+# the libraries to link against to use TIRPC.
+# ``LIBUNWIND_VERSION``
+# the version of TIRPC found.
+# ``LIBUNWIND_FOUND``
+# true if the TIRPC headers and libraries were found.
+#
+
+find_package(PkgConfig QUIET)
+pkg_check_modules(PC_LIBUNWIND libunwind)
+
+find_path(LIBUNWIND_INCLUDE_DIRS
+ NAMES libunwind.h
+ HINTS ${PC_LIBUNWIND_INCLUDE_DIRS}
+)
+
+find_library(LIBUNWIND_LIBRARIES
+ NAMES unwind
+ HINTS ${PC_LIBUNWIND_LIBRARY_DIRS}
+)
+
+set(LIBUNWIND_VERSION ${PC_LIBUNWIND_VERSION})
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(Libunwind
+ REQUIRED_VARS LIBUNWIND_LIBRARIES LIBUNWIND_INCLUDE_DIRS
+ VERSION_VAR LIBUNWIND_VERSION
+)
+
+mark_as_advanced(LIBUNWIND_INCLUDE_DIRS LIBUNWIND_LIBRARIES)
find_package(Flatbuffers QUIET)
find_package(ICONV QUIET)
find_package(UUID QUIET)
+find_package(Libunwind)
set (HAVE_ICONV "1")
endif()
+if (LIBUNWIND_FOUND)
+ # We don't actually use backtrace from libunwind, but it's basically the
+ # only symbol guaranteed to be present.
+ check_library_exists (${LIBUNWIND_LIBRARIES} backtrace "" HAVE_LIBUNWIND)
+endif()
+
if (SAFEC_FOUND)
check_library_exists (${SAFEC_LIBRARIES} printf_s "" HAVE_SAFEC)
endif()
/* Available libraries */
+/* flatbuffers available */
+#cmakedefine HAVE_FLATBUFFERS 1
+
/* hyperscan available */
#cmakedefine HAVE_HYPERSCAN 1
#cmakedefine HAVE_HS_COMPILE_LIT 1
+/* iconv available */
+#cmakedefine HAVE_ICONV 1
+
+/* libunwind available */
+#cmakedefine HAVE_LIBUNWIND 1
+
/* lzma available */
#cmakedefine HAVE_LZMA 1
/* safec available */
#cmakedefine HAVE_SAFEC 1
-#cmakedefine HAVE_FLATBUFFERS 1
-
-#cmakedefine HAVE_ICONV 1
-
+/* uuid available */
#cmakedefine HAVE_UUID 1
+/* tirpc should be used for RPC database lookups */
#cmakedefine USE_TIRPC 1
# parse arguments
while [ $# -ne 0 ]; do
case "$1" in
- -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
*) optarg= ;;
esac
* *iconv*: for converting UTF16-LE filenames to UTF8 (usually included in glibc)
+* *libunwind*: for printing a backtrace when a fatal signal is received.
+
* *lzma*: for decompression of SWF and PDF files.
* *safec*: for additional runtime error checking of some memory copy operations.
* dnet from https://github.com/dugsong/libdnet.git for network utility
functions
-* hwloc from https://www.open-mpi.org/projects/hwloc/ for CPU affinity management
+* hwloc from https://www.open-mpi.org/projects/hwloc/ for CPU affinity
+ management
* LuaJIT from http://luajit.org for configuration and scripting
* pcre from http://www.pcre.org for regular expression pattern matching
-* pkgconfig from https://www.freedesktop.org/wiki/Software/pkg-config/ to locate build dependencies
+* pkgconfig from https://www.freedesktop.org/wiki/Software/pkg-config/ to locate
+ build dependencies
* zlib from http://www.zlib.net for decompression (>= 1.2.8 recommended)
* iconv from https://ftp.gnu.org/pub/gnu/libiconv/ for converting
UTF16-LE filenames to UTF8 (usually included in glibc)
+* libunwind from https://www.nongnu.org/libunwind/ to attempt to dump a
+ somewhat readable backtrace when a fatal signal is received
+
* lzma >= 5.1.2 from http://tukaani.org/xz/ for decompression of SWF and
PDF files
LIST(APPEND EXTERNAL_LIBRARIES ${DAQ_STATIC_MODULE_LIBS})
endif ()
-if ( FLATBUFFERS_FOUND )
+if ( HAVE_FLATBUFFERS )
LIST(APPEND EXTERNAL_LIBRARIES ${FLATBUFFERS_LIBRARIES})
endif()
+if ( HAVE_HYPERSCAN )
+ LIST(APPEND EXTERNAL_LIBRARIES ${HS_LIBRARIES})
+ LIST(APPEND EXTERNAL_INCLUDES ${HS_INCLUDE_DIRS})
+endif ()
+
+if ( HAVE_ICONV )
+ LIST(APPEND EXTERNAL_LIBRARIES ${ICONV_LIBRARY})
+ LIST(APPEND EXTERNAL_INCLUDES ${ICONV_INCLUDE_DIR})
+endif ()
+
+if ( HAVE_LIBUNWIND )
+ LIST(APPEND EXTERNAL_LIBRARIES ${LIBUNWIND_LIBRARIES})
+ LIST(APPEND EXTERNAL_INCLUDES ${LIBUNWIND_INCLUDE_DIRS})
+endif ()
+
if ( HAVE_LZMA )
LIST(APPEND EXTERNAL_LIBRARIES ${LIBLZMA_LIBRARIES})
endif()
LIST(APPEND EXTERNAL_INCLUDES ${UUID_INCLUDE_DIR})
endif ()
-if ( HS_FOUND )
- LIST(APPEND EXTERNAL_LIBRARIES ${HS_LIBRARIES})
- LIST(APPEND EXTERNAL_INCLUDES ${HS_INCLUDE_DIRS})
-endif ()
-
-if ( ICONV_FOUND )
- LIST(APPEND EXTERNAL_LIBRARIES ${ICONV_LIBRARY})
- LIST(APPEND EXTERNAL_INCLUDES ${ICONV_INCLUDE_DIR})
-endif ()
-
if ( USE_TIRPC )
LIST(APPEND EXTERNAL_LIBRARIES ${TIRPC_LIBRARIES})
LIST(APPEND EXTERNAL_INCLUDES ${TIRPC_INCLUDE_DIRS})
process.h
ring.h
ring_logic.h
+ sigsafe.cc
+ sigsafe.h
scratch_allocator.cc
)
base64_encoder.cc
)
+add_catch_test( sigsafe_test
+ NO_TEST_SOURCE
+ SOURCES
+ sigsafe.cc
+)
+
add_subdirectory(test)
#include <fcntl.h>
-#if defined(HAVE_MALLOC_TRIM)
+#ifdef HAVE_LIBUNWIND
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#include <dlfcn.h>
+#endif
+
+#ifdef HAVE_MALLOC_TRIM
#include <malloc.h>
#endif
#include "log/messages.h"
#include "main.h"
+#include "main/build.h"
#include "main/oops_handler.h"
#include "main/snort_config.h"
#include "utils/stats.h"
#include "markup.h"
#include "ring.h"
+#include "sigsafe.h"
using namespace snort;
-using namespace std;
#ifndef SIGNAL_SNORT_RELOAD
#define SIGNAL_SNORT_RELOAD SIGHUP
static volatile sig_atomic_t child_ready_signal = 0;
static THREAD_LOCAL bool is_main_thread = false;
-typedef void (* sighandler_t)(int);
-static bool add_signal(int sig, sighandler_t, bool check_needed);
+// Backup copies of the original fatal signal actions. Kept here because they must
+// be trivially accessible to signal handlers.
+#ifdef HAVE_SIGACTION
+static struct
+{
+ int signal;
+ struct sigaction original_sigaction;
+}
+original_sigactions[] =
+{
+ { SIGABRT, { } },
+ { SIGBUS, { } },
+ { SIGSEGV, { } },
+ { 0, { } },
+};
+#else
+static struct
+{
+ int signal;
+ sighandler_t original_sighandler;
+}
+original_sighandlers[] =
+{
+ { SIGABRT, SIG_DFL },
+ { SIGBUS, SIG_DFL },
+ { SIGSEGV, SIG_DFL },
+ { 0, SIG_DFL },
+};
+#endif
+
+static bool add_signal(int sig, sighandler_t);
+static bool restore_signal(int sig, bool silent = false);
static bool exit_pronto = true;
void set_main_thread()
{ is_main_thread = true; }
-static void exit_log(const char* why)
-{
- // printf() etc not allowed here
- char buf[256] = "\n\0";
-
- strncat(buf, get_prompt(), sizeof(buf)-strlen(buf)-1);
- strncat(buf, " caught ", sizeof(buf)-strlen(buf)-1);
- strncat(buf, why, sizeof(buf)-strlen(buf)-1);
- strncat(buf, " signal, exiting\n", sizeof(buf)-strlen(buf)-1);
-
- (void)write(STDOUT_FILENO, buf, strlen(buf)); // FIXIT-W ignoring return value
-}
-
static void exit_handler(int signal)
{
PigSignal s;
switch ( signal )
{
- case SIGTERM: s = PIG_SIG_TERM; t = "term"; break;
- case SIGQUIT: s = PIG_SIG_QUIT; t = "quit"; break;
- case SIGINT: s = PIG_SIG_INT; t = "int"; break;
- default: return;
+ case SIGTERM: s = PIG_SIG_TERM; t = "term"; break;
+ case SIGQUIT: s = PIG_SIG_QUIT; t = "quit"; break;
+ case SIGINT: s = PIG_SIG_INT; t = "int"; break;
+ default: return;
}
if ( exit_pronto )
{
- exit_log(t);
+ SigSafePrinter(STDOUT_FILENO).printf("%s caught %s signal, exiting\n", get_prompt(), t);
_exit(0);
}
sig_ring.put(PIG_SIG_RELOAD_HOSTS);
}
-static void ignore_handler(int /*signal*/)
+static void child_ready_handler(int /*signal*/)
{
+ child_ready_signal = 1;
}
-static void child_ready_handler(int /*signal*/)
+#ifdef HAVE_LIBUNWIND
+static void print_backtrace(SigSafePrinter& ssp)
{
- child_ready_signal = 1;
+ int ret;
+
+ // grab the machine context and initialize the cursor
+ unw_context_t context;
+ if ((ret = unw_getcontext(&context)) < 0)
+ {
+ ssp.printf("unw_getcontext failed: %s (%d)\n", unw_strerror(ret), ret);
+ return;
+ }
+
+ unw_cursor_t cursor;
+ if ((ret = unw_init_local(&cursor, &context)) < 0)
+ {
+ ssp.printf("unw_init_local failed: %s (%d)\n", unw_strerror(ret), ret);
+ return;
+ }
+
+ ssp.printf("Backtrace:\n");
+
+ // walk the stack frames
+ unsigned frame_num = 0;
+ while ((ret = unw_step(&cursor)) > 0)
+ {
+ // skip printing any frames until we've found the frame that received the signal
+ if (frame_num == 0 && !unw_is_signal_frame(&cursor))
+ continue;
+
+ unw_word_t pc;
+ if ((ret = unw_get_reg(&cursor, UNW_REG_IP, &pc)) < 0)
+ {
+ ssp.printf("unw_get_reg failed for instruction pointer: %s (%d)\n",
+ unw_strerror(ret), ret);
+ return;
+ }
+
+ unw_proc_info_t pip;
+ if ((ret = unw_get_proc_info(&cursor, &pip)) < 0)
+ {
+ ssp.printf("unw_get_proc_info failed: %s (%d)\n", unw_strerror(ret), ret);
+ return;
+ }
+
+ ssp.printf(" #%u 0x%x", frame_num, pc);
+
+ char sym[256];
+ unw_word_t offset;
+ if (unw_get_proc_name(&cursor, sym, sizeof(sym), &offset) == 0)
+ ssp.printf(" in %s+0x%x", sym, offset);
+
+ Dl_info dlinfo;
+ if (dladdr((void *)(uintptr_t)(pip.start_ip + offset), &dlinfo)
+ && dlinfo.dli_fname && *dlinfo.dli_fname)
+ {
+ ssp.printf(" (%s @0x%x)", dlinfo.dli_fname, dlinfo.dli_fbase);
+ }
+
+ ssp.printf("\n");
+
+ frame_num++;
+ }
+ if (ret < 0)
+ ssp.printf("unw_step failed: %s (%d)\n", unw_strerror(ret), ret);
+
+ ssp.printf("\n");
}
+#endif
static void oops_handler(int signal)
{
+ // First things first, restore the original signal handler.
+ restore_signal(signal, true);
+
+ // Log the Snort version and signal caught.
+ const char* sigstr = "???\n";
+ switch (signal)
+ {
+ case SIGABRT:
+ sigstr = STRINGIFY(SIGABRT) " (" STRINGIFY_MX(SIGABRT) ")";
+ break;
+ case SIGBUS:
+ sigstr = STRINGIFY(SIGBUS) " (" STRINGIFY_MX(SIGBUS) ")";
+ break;
+ case SIGSEGV:
+ sigstr = STRINGIFY(SIGSEGV) " (" STRINGIFY_MX(SIGSEGV) ")";
+ break;
+ }
+ SigSafePrinter ssp(STDERR_FILENO);
+ ssp.printf("\nSnort (PID %u) caught fatal signal: %s\n", getpid(), sigstr);
+ ssp.printf("Version: " VERSION " Build " BUILD "\n\n");
+
+#ifdef HAVE_LIBUNWIND
+ // Try to pretty-print a stack trace using libunwind to traverse the stack.
+ print_backtrace(ssp);
+#endif
+
// FIXIT-L what should we capture if this is the main thread?
if ( !is_main_thread )
- OopsHandler::handle_crash();
+ OopsHandler::handle_crash(STDERR_FILENO);
- add_signal(signal, SIG_DFL, false);
+ // Finally, raise the signal so that the original handler can handle it.
raise(signal);
}
// signal management
//-------------------------------------------------------------------------
-// If check needed, also check whether previous signal_handler is neither
-// SIG_IGN nor SIG_DFL
-
-// FIXIT-L convert sigaction, etc. to c++11
-static bool add_signal(int sig, sighandler_t signal_handler, bool check_needed)
+static bool add_signal(int sig, sighandler_t signal_handler)
{
- sighandler_t pre_handler;
-
#ifdef HAVE_SIGACTION
struct sigaction action;
- struct sigaction old_action;
+ // Mask all other signals while in the signal handler
sigfillset(&action.sa_mask);
+ // Make compatible system calls restartable across signals
action.sa_flags = SA_RESTART;
action.sa_handler = signal_handler;
- sigaction(sig, &action, &old_action);
- pre_handler = old_action.sa_handler;
+
+ struct sigaction* old_action = nullptr;
+ for (unsigned i = 0; original_sigactions[i].signal; i++)
+ {
+ if (original_sigactions[i].signal == sig)
+ {
+ old_action = &original_sigactions[i].original_sigaction;
+ break;
+ }
+ }
+
+ if (sigaction(sig, &action, old_action) != 0)
+ {
+ ErrorMessage("Could not add handler for signal %d: %s (%d)\n", sig, get_error(errno), errno);
+ return false;
+ }
#else
- pre_handler = signal(sig, signal_handler);
+ sighandler_t original_handler = signal(sig, signal_handler);
+ if (original_handler == SIG_ERR)
+ {
+ ErrorMessage("Could not add handler for signal %d: %s (%d)\n", sig, get_error(errno), errno);
+ return false;
+ }
+
+ for (unsigned i = 0; original_sighandlers[i].signal; i++)
+ {
+ if (original_sigactions[i].signal == sig)
+ {
+ original_sigactions[i].original_handler = original_handler;
+ break;
+ }
+ }
#endif
- if (SIG_ERR == pre_handler)
+
+ return true;
+}
+
+static bool restore_signal(int sig, bool silent)
+{
+#ifdef HAVE_SIGACTION
+ struct sigaction* new_action = nullptr;
+ struct sigaction action;
+
+ for (unsigned i = 0; original_sigactions[i].signal; i++)
+ {
+ if (original_sigactions[i].signal == sig)
+ {
+ new_action = &original_sigactions[i].original_sigaction;
+ break;
+ }
+ }
+
+ if (!new_action)
+ {
+ sigemptyset(&action.sa_mask);
+ action.sa_flags = 0;
+ action.sa_handler = SIG_DFL;
+ new_action = &action;
+ }
+
+ if (sigaction(sig, new_action, nullptr) != 0)
{
- ParseError("Could not add handler for signal %d \n", sig);
+ if (!silent)
+ ErrorMessage("Could not restore handler for signal %d: %s (%d)\n", sig, get_error(errno), errno);
return false;
}
- else if (check_needed && (SIG_IGN != pre_handler) && (SIG_DFL!= pre_handler))
+#else
+ sighandler_t signal_handler = SIG_DFL;
+
+ for (unsigned i = 0; original_sighandlers[i].signal; i++)
+ {
+ if (original_sigactions[i].signal == sig)
+ {
+ signal_handler = original_sigactions[i].original_handler;
+ break;
+ }
+ }
+
+ if (signal(sig, signal_handler) == SIG_ERR)
{
- ParseWarning(WARN_CONF, "handler is already installed for signal %d.\n", sig);
+ if (!silent)
+ ErrorMessage("Could not restore handler for signal %d: %s (%d)\n", sig, get_error(errno), errno);
+ return false;
}
+#endif
+
return true;
}
// FIXIT-L this is undefined for multithreaded apps
sigprocmask(SIG_SETMASK, &set, nullptr);
- /* Make this prog behave nicely when signals come along.
- * Windows doesn't like all of these signals, and will
- * set errno for some. Ignore/reset this error so it
- * doesn't interfere with later checks of errno value. */
- add_signal(SIGTERM, exit_handler, true);
- add_signal(SIGINT, exit_handler, true);
- add_signal(SIGQUIT, dirty_handler, true);
+ // Make this program behave nicely when signals come along.
+ add_signal(SIGTERM, exit_handler);
+ add_signal(SIGINT, exit_handler);
+ add_signal(SIGQUIT, dirty_handler);
- add_signal(SIGNAL_SNORT_DUMP_STATS, dump_stats_handler, true);
- add_signal(SIGNAL_SNORT_ROTATE_STATS, rotate_stats_handler, true);
- add_signal(SIGNAL_SNORT_RELOAD, reload_config_handler, true);
- add_signal(SIGNAL_SNORT_READ_ATTR_TBL, reload_attrib_handler, true);
+ add_signal(SIGNAL_SNORT_DUMP_STATS, dump_stats_handler);
+ add_signal(SIGNAL_SNORT_ROTATE_STATS, rotate_stats_handler);
+ add_signal(SIGNAL_SNORT_RELOAD, reload_config_handler);
+ add_signal(SIGNAL_SNORT_READ_ATTR_TBL, reload_attrib_handler);
- add_signal(SIGPIPE, ignore_handler, true);
- add_signal(SIGABRT, oops_handler, true);
- add_signal(SIGSEGV, oops_handler, true);
- add_signal(SIGBUS, oops_handler, true);
+ add_signal(SIGPIPE, SIG_IGN);
+ add_signal(SIGABRT, oops_handler);
+ add_signal(SIGSEGV, oops_handler);
+ add_signal(SIGBUS, oops_handler);
errno = 0;
}
void term_signals()
{
- add_signal(SIGTERM, SIG_DFL, false);
- add_signal(SIGINT, SIG_DFL, false);
- add_signal(SIGQUIT, SIG_DFL, false);
-
- add_signal(SIGNAL_SNORT_DUMP_STATS, SIG_DFL, false);
- add_signal(SIGNAL_SNORT_ROTATE_STATS, SIG_DFL, false);
- add_signal(SIGNAL_SNORT_RELOAD, SIG_DFL, false);
- add_signal(SIGNAL_SNORT_READ_ATTR_TBL, SIG_DFL, false);
-
- add_signal(SIGPIPE, SIG_DFL, false);
- add_signal(SIGABRT, SIG_DFL, false);
- add_signal(SIGSEGV, SIG_DFL, false);
- add_signal(SIGBUS, SIG_DFL, false);
+ restore_signal(SIGTERM);
+ restore_signal(SIGINT);
+ restore_signal(SIGQUIT);
+
+ restore_signal(SIGNAL_SNORT_DUMP_STATS);
+ restore_signal(SIGNAL_SNORT_ROTATE_STATS);
+ restore_signal(SIGNAL_SNORT_RELOAD);
+ restore_signal(SIGNAL_SNORT_READ_ATTR_TBL);
+
+ restore_signal(SIGPIPE);
+ restore_signal(SIGABRT);
+ restore_signal(SIGSEGV);
+ restore_signal(SIGBUS);
}
static void help_signal(unsigned n, const char* name, const char* h)
{
- cout << Markup::item();
+ std::cout << Markup::item();
- cout << Markup::emphasis_on();
- cout << name;
- cout << Markup::emphasis_off();
+ std::cout << Markup::emphasis_on();
+ std::cout << name;
+ std::cout << Markup::emphasis_off();
- cout << "(" << n << "): " << h << endl;
+ std::cout << "(" << n << "): " << h << std::endl;
}
void help_signals()
LogMessage("initializing daemon mode\n");
// register signal handler so that parent can trap signal
- add_signal(SIGNAL_SNORT_CHILD_READY, child_ready_handler, true);
+ add_signal(SIGNAL_SNORT_CHILD_READY, child_ready_handler);
pid_t cpid = fork();
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+// sigsafe.cc author Michael Altizer <mialtize@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "sigsafe.h"
+
+#include <unistd.h>
+
+#include <cassert>
+#include <cstdarg>
+#include <cstring>
+
+/*
+ * Signal safety of functions called from here (POSIX async-signal-safe requirement):
+ * strlen POSIX.1-2016
+ * write POSIX.1-2001
+ */
+
+static int sigsafe_format_uint64_dec(uint64_t num, char* buf, size_t buf_len)
+{
+ uint64_t divisor;
+ size_t len;
+
+ // ceil(log10(0xFFFFFFFFFFFFFFFF)) = 20 + 1 for '\0'
+ if (buf_len > 21)
+ buf_len = 21;
+
+ for (len = 1, divisor = 10;
+ len < buf_len - 1 && num / divisor;
+ len++, divisor *= 10);
+
+ for (size_t i = len, divisor = 1; i > 0; i--, divisor *= 10)
+ buf[i - 1] = '0' + ((num / divisor) % 10);
+
+ buf[len] = '\0';
+ return len;
+}
+
+static int sigsafe_format_uint64_hex(uint64_t num, char* buf, size_t buf_len)
+{
+ uint64_t divisor;
+ size_t len;
+
+ // log16(0xFFFFFFFFFFFFFFFF) = 16 + 1 for '\0'
+ if (buf_len > 17)
+ buf_len = 17;
+
+ for (len = 1, divisor = 0x10;
+ len < buf_len - 1 && num / divisor;
+ len++, divisor *= 0x10);
+
+ for (size_t i = len, divisor = 1; i > 0; i--, divisor *= 0x10)
+ {
+ int val = (num / divisor) % 0x10;
+
+ if (val < 10)
+ buf[i - 1] = '0' + val;
+ else
+ buf[i - 1] = 'a' + val - 10;
+ }
+
+ buf[len] = '\0';
+ return len;
+}
+
+static void sigsafe_vsnprintf(char* str, size_t size, const char* format, va_list ap)
+{
+ size_t fmt_idx = 0;
+ size_t str_idx = 0;
+ size_t fmt_len = strlen(format);
+ char number[32];
+ const char* string_arg;
+ int64_t i64_arg;
+ uint64_t u64_arg;
+ size_t arg_len;
+ bool negative;
+
+ if (size == 0)
+ return;
+
+ for (; fmt_idx < fmt_len && str_idx < size - 1; fmt_idx++)
+ {
+ if (format[fmt_idx] != '%')
+ {
+ str[str_idx++] = format[fmt_idx];
+ continue;
+ }
+ if (++fmt_idx >= fmt_len)
+ break;
+
+ bool zpad = false;
+ if (format[fmt_idx] == '0')
+ {
+ zpad = true;
+ if (++fmt_idx >= fmt_len)
+ break;
+ }
+
+ size_t min_width = 0;
+ if (format[fmt_idx] >= '1' && format[fmt_idx] <= '9')
+ {
+ while (format[fmt_idx] >= '0' && format[fmt_idx] <= '9')
+ {
+ min_width = min_width * 10 + (format[fmt_idx] - '0');
+ if (++fmt_idx >= fmt_len)
+ break;
+ }
+ if (fmt_idx >= fmt_len)
+ break;
+ }
+
+ switch(format[fmt_idx])
+ {
+ case 'd':
+ i64_arg = va_arg(ap, int64_t);
+ if (i64_arg < 0)
+ {
+ negative = true;
+ u64_arg = -i64_arg;
+ }
+ else
+ {
+ negative = false;
+ u64_arg = i64_arg;
+ }
+ arg_len = sigsafe_format_uint64_dec(u64_arg, number, sizeof(number));
+ if (arg_len < min_width)
+ {
+ for (size_t padding = arg_len; padding < min_width && str_idx < size - 1; padding++)
+ {
+ if (negative && ((padding == arg_len && zpad) || (padding + 1 == min_width && !zpad)))
+ str[str_idx++] = '-';
+ else
+ str[str_idx++] = zpad ? '0' : ' ';
+ }
+ }
+ else if (negative && str_idx < size - 1)
+ str[str_idx++] = '-';
+ for (size_t arg_idx = 0; arg_idx < arg_len && str_idx < size - 1; arg_idx++)
+ str[str_idx++] = number[arg_idx];
+ break;
+
+ case 'u':
+ u64_arg = va_arg(ap, uint64_t);
+ arg_len = sigsafe_format_uint64_dec(u64_arg, number, sizeof(number));
+ for (size_t padding = arg_len; padding < min_width && str_idx < size - 1; padding++)
+ str[str_idx++] = zpad ? '0' : ' ';
+ for (size_t arg_idx = 0; arg_idx < arg_len && str_idx < size - 1; arg_idx++)
+ str[str_idx++] = number[arg_idx];
+ break;
+
+ case 's':
+ string_arg = va_arg(ap, const char*);
+ if (!string_arg)
+ string_arg = "(null)";
+ arg_len = strlen(string_arg);
+ for (size_t padding = arg_len; padding < min_width && str_idx < size - 1; padding++)
+ str[str_idx++] = ' ';
+ for (size_t arg_idx = 0; arg_idx < arg_len && str_idx < size - 1; arg_idx++)
+ str[str_idx++] = string_arg[arg_idx];
+ break;
+
+ case 'x':
+ u64_arg = va_arg(ap, uint64_t);
+ arg_len = sigsafe_format_uint64_hex(u64_arg, number, sizeof(number));
+ for (size_t padding = arg_len; padding < min_width && str_idx < size - 1; padding++)
+ str[str_idx++] = zpad ? '0' : ' ';
+ for (size_t arg_idx = 0; arg_idx < arg_len && str_idx < size - 1; arg_idx++)
+ str[str_idx++] = number[arg_idx];
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ str[str_idx] = '\0';
+}
+
+static void sigsafe_snprintf(char* str, size_t size, const char* format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ sigsafe_vsnprintf(str, size, format, ap);
+ va_end(ap);
+}
+
+SigSafePrinter::SigSafePrinter(char *buf, size_t size) : buf(buf), buf_size(size)
+{
+ buf[0] = '\0';
+}
+
+void SigSafePrinter::write_string(const char* str)
+{
+ size_t len = strlen(str);
+ if (fd >= 0)
+ write(fd, str, len);
+ else if (buf)
+ {
+ if (len > buf_size - buf_idx - 1)
+ len = buf_size - buf_idx - 1;
+ strncpy(buf + buf_idx, str, len);
+ buf_idx += len;
+ buf[buf_idx] = '\0';
+ }
+}
+
+void SigSafePrinter::hex_dump(const uint8_t* data, unsigned len)
+{
+ char line[41];
+ unsigned lidx = 0;
+
+ for (unsigned i = 0; i < len; i++)
+ {
+ if (i > 0)
+ {
+ if (i % 16 == 0)
+ {
+ line[lidx++] = '\n';
+ line[lidx] = '\0';
+ write_string(line);
+ lidx = 0;
+ }
+ else if (i % 2 == 0)
+ line[lidx++] = ' ';
+ }
+ sigsafe_snprintf(line + lidx, sizeof(line) - lidx, "%02x", data[i]);
+ lidx += 2;
+ }
+ if (lidx)
+ {
+ line[lidx++] = '\n';
+ line[lidx] = '\0';
+ write_string(line);
+ }
+}
+
+void SigSafePrinter::printf(const char *format, ...)
+{
+ char fmt_buf[1024];
+ va_list ap;
+ va_start(ap, format);
+ sigsafe_vsnprintf(fmt_buf, sizeof(fmt_buf), format, ap);
+ va_end(ap);
+ write_string(fmt_buf);
+}
+
+#ifdef CATCH_TEST_BUILD
+
+#include <cinttypes>
+
+#include "catch/catch.hpp"
+
+TEST_CASE("sigsafe printer", "[SigsafePrinter]")
+{
+ using Catch::Matchers::Equals;
+
+ uint64_t unsigned_tests[] =
+ {
+ 0, // Zero
+ 5, // Single digit number
+ 12, // Two digit decimal number
+ 37, // Two digit hex number
+ 0xC90B2, // Large < 32 bit number
+ 0x15D027BF211B37A, // Large > 32 bit number
+ 0xFFFFFFFFFFFFFFFF, // Maximum 64-bit number
+ };
+ int64_t signed_tests[] =
+ {
+ 0, // Zero
+ 5, // Single digit number
+ 12, // Two digit decimal number
+ 37, // Two digit hex number
+ 0xC90B2, // Large < 32 bit number
+ 0x15D027BF211B37A, // Large > 32 bit number
+ 0x7FFFFFFFFFFFFFFF, // Maximum 64-bit signed number
+ -1, // Single digit number
+ -12, // Two digit decimal number
+ -0xC90B2, // Large < 32 bit number
+ -0x15D027BF211B37A, // Large > 32 bit number
+ -0x7FFFFFFFFFFFFFFF, // Maximum 64-bit signed number
+ } ;
+ char expected[1024];
+ char actual[1024];
+ SECTION("unsigned decimal")
+ {
+ for (size_t i = 0; i < sizeof(unsigned_tests) / sizeof(*unsigned_tests); i++)
+ {
+ snprintf(expected, sizeof(expected), "%" PRIu64, unsigned_tests[i]);
+ SigSafePrinter(actual, sizeof(actual)).printf("%u", unsigned_tests[i]);
+ CHECK_THAT(expected, Equals(actual));
+ }
+ }
+ SECTION("padded unsigned decimal")
+ {
+ for (size_t i = 0; i < sizeof(unsigned_tests) / sizeof(*unsigned_tests); i++)
+ {
+ snprintf(expected, sizeof(expected), "%32" PRIu64, unsigned_tests[i]);
+ SigSafePrinter(actual, sizeof(actual)).printf("%32u", unsigned_tests[i]);
+ CHECK_THAT(expected, Equals(actual));
+ }
+ }
+ SECTION("0-padded unsigned decimal")
+ {
+ for (size_t i = 0; i < sizeof(unsigned_tests) / sizeof(*unsigned_tests); i++)
+ {
+ snprintf(expected, sizeof(expected), "%032" PRIu64, unsigned_tests[i]);
+ SigSafePrinter(actual, sizeof(actual)).printf("%032u", unsigned_tests[i]);
+ CHECK_THAT(expected, Equals(actual));
+ }
+ }
+ SECTION("signed decimal")
+ {
+ for (size_t i = 0; i < sizeof(signed_tests) / sizeof(*signed_tests); i++)
+ {
+ snprintf(expected, sizeof(expected), "%" PRId64, signed_tests[i]);
+ SigSafePrinter(actual, sizeof(actual)).printf("%d", signed_tests[i]);
+ CHECK_THAT(expected, Equals(actual));
+ }
+ }
+ SECTION("padded signed decimal")
+ {
+ for (size_t i = 0; i < sizeof(signed_tests) / sizeof(*signed_tests); i++)
+ {
+ snprintf(expected, sizeof(expected), "%32" PRId64, signed_tests[i]);
+ SigSafePrinter(actual, sizeof(actual)).printf("%32d", signed_tests[i]);
+ CHECK_THAT(expected, Equals(actual));
+ }
+ }
+ SECTION("0-padded signed decimal")
+ {
+ for (size_t i = 0; i < sizeof(signed_tests) / sizeof(*signed_tests); i++)
+ {
+ snprintf(expected, sizeof(expected), "%032" PRId64, signed_tests[i]);
+ SigSafePrinter(actual, sizeof(actual)).printf("%032d", signed_tests[i]);
+ CHECK_THAT(expected, Equals(actual));
+ }
+ }
+ SECTION("hex")
+ {
+ for (size_t i = 0; i < sizeof(unsigned_tests) / sizeof(*unsigned_tests); i++)
+ {
+ snprintf(expected, sizeof(expected), "%" PRIx64, unsigned_tests[i]);
+ SigSafePrinter(actual, sizeof(actual)).printf("%x", unsigned_tests[i]);
+ CHECK_THAT(expected, Equals(actual));
+ }
+ }
+ SECTION("padded hex")
+ {
+ for (size_t i = 0; i < sizeof(unsigned_tests) / sizeof(*unsigned_tests); i++)
+ {
+ snprintf(expected, sizeof(expected), "%2" PRIx64, unsigned_tests[i]);
+ SigSafePrinter(actual, sizeof(actual)).printf("%2x", unsigned_tests[i]);
+ CHECK_THAT(expected, Equals(actual));
+ }
+ }
+ SECTION("0-padded hex")
+ {
+ for (size_t i = 0; i < sizeof(unsigned_tests) / sizeof(*unsigned_tests); i++)
+ {
+ snprintf(expected, sizeof(expected), "%02" PRIx64, unsigned_tests[i]);
+ SigSafePrinter(actual, sizeof(actual)).printf("%02x", unsigned_tests[i]);
+ CHECK_THAT(expected, Equals(actual));
+ }
+ }
+ SECTION("string")
+ {
+ snprintf(expected, sizeof(expected), "%s", "foobar");
+ SigSafePrinter(actual, sizeof(actual)).printf("%s", "foobar");
+ CHECK_THAT(expected, Equals(actual));
+ }
+ SECTION("null string")
+ {
+ const char* nullstr = nullptr;
+ snprintf(expected, sizeof(expected), "%s", nullstr);
+ SigSafePrinter(actual, sizeof(actual)).printf("%s", nullstr);
+ CHECK_THAT(expected, Equals(actual));
+ }
+ SECTION("padded string")
+ {
+ snprintf(expected, sizeof(expected), "%32s", "foobar");
+ SigSafePrinter(actual, sizeof(actual)).printf("%32s", "foobar");
+ CHECK_THAT(expected, Equals(actual));
+ }
+ SECTION("all together now")
+ {
+ for (size_t i = 0; i < sizeof(unsigned_tests) / sizeof(*unsigned_tests); i++)
+ {
+ snprintf(expected, sizeof(expected), "%" PRIu64 " is %" PRIx64 " in %s!",
+ unsigned_tests[i], unsigned_tests[i], "hex");
+ SigSafePrinter(actual, sizeof(actual)).printf("%u is %x in %s!",
+ unsigned_tests[i], unsigned_tests[i], "hex");
+ CHECK_THAT(expected, Equals(actual));
+ }
+ }
+ SECTION("unrecognized conversion specifiers")
+ {
+ char format[32];
+ snprintf(expected, sizeof(expected), "Nothing to see here: .");
+ for (char c = 'A'; c < 'z'; c++)
+ {
+ if (c == 'd' || c == 's' || c == 'u' || c == 'x')
+ continue;
+ snprintf(format, sizeof(format), "Nothing to see here: %%%c.", c);
+ SigSafePrinter(actual, sizeof(actual)).printf(format, 0xDEADBEEF);
+ CHECK_THAT(expected, Equals(actual));
+ }
+ }
+ SECTION("hexdump")
+ {
+ uint8_t data[32];
+ unsigned offset = 0;
+ for (unsigned i = 0; i < sizeof(data); i++)
+ {
+ data[i] = i;
+ if (i > 0)
+ {
+ if (i % 16 == 0)
+ offset += snprintf(expected + offset, sizeof(expected) - offset, "\n");
+ else if (i % 2 == 0)
+ offset += snprintf(expected + offset, sizeof(expected) - offset, " ");
+ }
+ offset += snprintf(expected + offset, sizeof(expected) - offset, "%02x", data[i]);
+ }
+ snprintf(expected + offset, sizeof(expected) - offset, "\n");
+ SigSafePrinter(actual, sizeof(actual)).hex_dump(data, sizeof(data));
+ CHECK_THAT(expected, Equals(actual));
+ }
+}
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+// sigsafe.h author Michael Altizer <mialtize@cisco.com>
+
+#ifndef SIGSAFE_H
+#define SIGSAFE_H
+
+#include <cstddef>
+#include <cstdint>
+
+class SigSafePrinter
+{
+public:
+ SigSafePrinter(char *buf, size_t size);
+ SigSafePrinter(int fd) : fd(fd) { }
+
+ void hex_dump(const uint8_t* data, unsigned len);
+ void printf(const char* format, ...);
+
+private:
+ void write_string(const char* str);
+
+private:
+ char* buf = nullptr;
+ size_t buf_size = 0;
+ size_t buf_idx = 0;
+ int fd = -1;
+};
+
+#endif
+
if (verdict == DAQ_VERDICT_BLOCK or verdict == DAQ_VERDICT_BLACKLIST)
p->active->send_reason_to_daq(*p);
+ oops_handler->set_current_message(nullptr);
p->pkth = nullptr; // No longer avail after finalize_message.
{
switcher->start();
Packet* p = switcher->get_context()->packet;
- oops_handler->set_current_packet(p);
p->context->wire_packet = p;
p->context->packet_number = get_packet_number();
set_default_policy(p->context->conf);
switcher->stop();
}
- oops_handler->set_current_packet(nullptr);
Stream::handle_timeouts(false);
HighAvailabilityManager::process_receive();
}
void Analyzer::process_daq_msg(DAQ_Msg_h msg, bool retry)
{
+ oops_handler->set_current_message(msg);
DAQ_Verdict verdict = DAQ_VERDICT_PASS;
switch (daq_msg_get_type(msg))
{
}
break;
}
+ oops_handler->set_current_message(nullptr);
{
Profile profile(daqPerfStats);
daq_instance->finalize_message(msg, verdict);
HighAvailabilityManager::thread_term();
SideChannelManager::thread_term();
- oops_handler->set_current_packet(nullptr);
+ oops_handler->set_current_message(nullptr);
daq_instance->stop();
SFDAQ::set_local_instance(nullptr);
exit_after_cnt = msg_cnt;
source = s ? s : "";
daq_instance = instance;
+ oops_handler = new OopsHandler();
retry_queue = new RetryQueue(200);
set_state(State::NEW);
}
void Analyzer::operator()(Swapper* ps, uint16_t run_num)
{
- oops_handler = new OopsHandler();
+ oops_handler->tinit();
set_thread_type(STHREAD_TYPE_PACKET);
set_instance_id(id);
term();
set_state(State::STOPPED);
+
+ oops_handler->tterm();
}
/* Note: This will be called from the main thread. Everything it does must be
#include "oops_handler.h"
-#include "protocols/packet.h"
+#include <daq.h>
+
+#include <cassert>
+#include <cstring>
+
+#include "helpers/sigsafe.h"
+
+#include "thread.h"
static THREAD_LOCAL OopsHandler* local_oops_handler = nullptr;
-void OopsHandler::handle_crash()
+void OopsHandler::handle_crash(int fd)
{
if (local_oops_handler)
- local_oops_handler->eternalize();
+ local_oops_handler->eternalize(fd);
}
-OopsHandler::OopsHandler()
+void OopsHandler::tinit()
{
assert(local_oops_handler == nullptr);
local_oops_handler = this;
}
-OopsHandler::~OopsHandler()
+void OopsHandler::tterm()
{
local_oops_handler = nullptr;
}
-void OopsHandler::eternalize()
+void OopsHandler::eternalize(int fd)
{
- // Copy the crashed thread's data. C++11 specs ensure the
- // thread that segfaulted will still be running.
- if (packet && packet->pkth)
- {
- pkth = *(packet->pkth);
- if (packet->pkt)
- {
- memcpy(data, packet->pkt, 0xFFFF & packet->pktlen);
- packet->pkt = data;
- }
- }
+ if (!msg)
+ return;
+
+ // Copy the crashed thread's data. C++11 specs ensure the thread that segfaulted will
+ // still be running.
+ // Signal safety of functions called from here (POSIX async-signal-safe requirement):
+ // memcpy POSIX.1-2016
+ type = daq_msg_get_type(msg);
+ header_len = daq_msg_get_hdr_len(msg);
+ memcpy(header, daq_msg_get_hdr(msg), std::min<size_t>(header_len, sizeof(header)));
+ data_len = daq_msg_get_data_len(msg);
+ memcpy(data, daq_msg_get_data(msg), std::min<size_t>(data_len, sizeof(data)));
+
+ if (fd < 0)
+ return;
+
+ // Dump the eternalized information to the file descriptor for coreless debugging
+ SigSafePrinter ssp(fd);
+ ssp.printf("= Current DAQ Message (Type %u) =\n\n", static_cast<uint64_t>(type));
+ ssp.printf("== Header (%u) ==\n", header_len);
+ ssp.hex_dump(header, header_len);
+ ssp.printf("\n== Data (%u) ==\n", data_len);
+ ssp.hex_dump(data, data_len);
+ ssp.printf("\n");
}
#include <daq_common.h>
-namespace snort
-{
-struct Packet;
-}
-
class OopsHandler
{
public:
- static void handle_crash();
+ static void handle_crash(int fd);
+
+ OopsHandler() = default;
+ ~OopsHandler() = default;
- OopsHandler();
- ~OopsHandler();
- void set_current_packet(snort::Packet* p) { packet = p; }
- void eternalize();
+ void tinit();
+ void set_current_message(DAQ_Msg_h cm) { msg = cm; }
+ void tterm();
+
+private:
+ void eternalize(int fd);
private:
- DAQ_PktHdr_t pkth = { };
- uint8_t data[65535] = { };
- snort::Packet* packet = nullptr;
+ DAQ_Msg_h msg = nullptr;
+ // Eternalized data
+ DAQ_MsgType type = static_cast<DAQ_MsgType>(0);
+ uint8_t header[UINT16_MAX] = { };
+ size_t header_len = 0;
+ uint8_t data[UINT16_MAX] = { };
+ uint32_t data_len = 0;
};
#endif
void ThreadConfig::implement_thread_affinity(SThreadType, unsigned) { }
void Swapper::apply(Analyzer&) { }
Swapper::~Swapper() { }
-OopsHandler::OopsHandler() { }
-OopsHandler::~OopsHandler() { }
+void OopsHandler::tinit() { }
+void OopsHandler::tterm() { }
uint16_t get_run_num() { return 0; }
void set_run_num(uint16_t) { }
void set_instance_id(unsigned) { }
-if ( FLATBUFFERS_FOUND )
+if ( HAVE_FLATBUFFERS )
set( FLATBUFFERS_SOURCE fbs_formatter.h fbs_formatter.cc )
endif()
endif (STATIC_INSPECTORS)
-if ( FLATBUFFERS_FOUND )
+if ( HAVE_FLATBUFFERS )
target_include_directories( perf_monitor PRIVATE ${FLATBUFFERS_INCLUDE_DIR} )
endif()
perf_formatter.cc
)
-if ( FLATBUFFERS_FOUND )
+if ( HAVE_FLATBUFFERS )
add_catch_test( fbs_formatter_test
NO_TEST_SOURCE
SOURCES
${TEST_FILES}
)
-if ( FLATBUFFERS_FOUND )
+if ( HAVE_FLATBUFFERS )
target_include_directories( utils PRIVATE ${FLATBUFFERS_INCLUDE_DIR} )
endif()
-if ( FLATBUFFERS_FOUND )
+if ( HAVE_FLATBUFFERS )
add_executable( fbstreamer
fbstreamer.cc
)