]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #2162 in SNORT/snort3 from ~MIALTIZE/snort3:signals to master
authorMichael Altizer (mialtize) <mialtize@cisco.com>
Tue, 23 Jun 2020 23:25:24 +0000 (23:25 +0000)
committerMichael Altizer (mialtize) <mialtize@cisco.com>
Tue, 23 Jun 2020 23:25:24 +0000 (23:25 +0000)
Squashed commit of the following:

commit 6a67fa549c3f42cd084d0e99a3d4326b3e89b7eb
Author: Michael Altizer <mialtize@cisco.com>
Date:   Wed Jun 17 17:57:21 2020 -0400

    cmake: Properly handle SIGNAL_SNORT_* options in configure_cmake.sh

commit 829d1dff292f417db11aee43615be745f7949eb6
Author: Michael Altizer <mialtize@cisco.com>
Date:   Tue May 26 17:43:27 2020 -0400

    helpers: Add support for dumping a backtrace via libunwind on fatal signals

    Support for this requires the libunwind development headers and library
    available at build time.  The dependency is optional.

commit 26b3d8171a7566141b32b411695e55e6a6ab4307
Author: Michael Altizer <mialtize@cisco.com>
Date:   Tue May 26 17:46:27 2020 -0400

    helpers: Dump additional information to stderr when a fatal signal is received

    This information includes which signal was received, the Snort version,
    and the current DAQ message information (if the signal was received
    while processing a message in a packet thread).

commit 8acc840fb0185b17957dcaea35ef43346a9502fd
Author: Michael Altizer <mialtize@cisco.com>
Date:   Tue May 26 17:46:13 2020 -0400

    helpers: Add a signal-safe formatted printing utility class

commit f2fee6377a6325a640e4ea0a858a78edb8e7a6c5
Author: Michael Altizer <mialtize@cisco.com>
Date:   Mon Apr 13 10:23:26 2020 -0400

    oops_handler: Operate on DAQ message instead of Snort Packets

commit ff7961a1b5e2315401dbe0be7741346aa1ceb37b
Author: Michael Altizer <mialtize@cisco.com>
Date:   Tue Mar 3 10:21:04 2020 -0500

    helpers: Revamp signal handler installation and removal

    Importantly, back up the previous signal handlers for fatal signals so
    that we can attempt to reinstall and call them on the way out.  This
    cleans up the interaction with libasan's SIGSEGV handler, for example.

commit ed6bccf52f0bb7da4b9676af5fec4a0452e6734e
Author: Michael Altizer <mialtize@cisco.com>
Date:   Mon Mar 16 11:41:34 2020 -0400

    build: Use sanity check results (HAVE_*) for optional packages in CMake

20 files changed:
CMakeLists.txt
cmake/FindLibunwind.cmake [new file with mode: 0644]
cmake/include_libraries.cmake
cmake/sanity_checks.cmake
config.cmake.h.in
configure_cmake.sh
doc/building.txt
doc/tutorial.txt
src/CMakeLists.txt
src/helpers/CMakeLists.txt
src/helpers/process.cc
src/helpers/sigsafe.cc [new file with mode: 0644]
src/helpers/sigsafe.h [new file with mode: 0644]
src/main/analyzer.cc
src/main/oops_handler.cc
src/main/oops_handler.h
src/main/test/stubs.h
src/network_inspectors/perf_monitor/CMakeLists.txt
src/utils/CMakeLists.txt
tools/flatbuffers/CMakeLists.txt

index d305e1a32e6f383c0544c40eccc81259ec39bf22..8ba3c53425a1be65af3ccfdee7ed94f8d44ea8a8 100644 (file)
@@ -125,6 +125,14 @@ else ()
     ICONV:          OFF")
 endif ()
 
+if (HAVE_LIBUNWIND)
+    message("\
+    Libunwind:      ON")
+else ()
+    message("\
+    Libunwind:      OFF")
+endif ()
+
 if (HAVE_LZMA)
     message("\
     LZMA:           ON")
diff --git a/cmake/FindLibunwind.cmake b/cmake/FindLibunwind.cmake
new file mode 100644 (file)
index 0000000..f66800a
--- /dev/null
@@ -0,0 +1,43 @@
+# 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)
index 401302f151fbd59156344aa9dc87494f32d4fd50..811c95ac6709948eb22e1980e72c9d7a8df45511 100644 (file)
@@ -25,3 +25,4 @@ endif (ENABLE_SAFEC)
 find_package(Flatbuffers QUIET)
 find_package(ICONV QUIET)
 find_package(UUID QUIET)
+find_package(Libunwind)
index de43a4bb0515d337cd28dbd1053a6cf66010a7b7..a748be22803815c38400f13d34cde2a55bb7cbe1 100644 (file)
@@ -150,6 +150,12 @@ if (ICONV_FOUND)
     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()
index d6330bc214e2d3ffed12ee837afbe34a65bcf8e6..56b42fe960086826ef570a249de21fd9323b07b6 100644 (file)
 
 /*  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
 
 
index 64d97e895ec9dcf786c9fd95089bdae54d16907b..50cc6fc5db241d690347264195319a947ad4945a 100755 (executable)
@@ -145,7 +145,7 @@ append_cache_entry CMAKE_INSTALL_PREFIX PATH   $prefix
 # 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
 
index c694d1c02f1d116bce13fddcb2cff65ed49ad7cb..80f340cd6200d56bb0a3b9f4fbf55d5535bc0136 100644 (file)
@@ -18,6 +18,8 @@ present.  There is no need to explicitly enable.
 
 * *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.
index fab457033915e6623986e44f502b5b6cfdbba901..c5ba545ed67f0b24a07fdbff51e82983d4c4d37b 100644 (file)
@@ -16,7 +16,8 @@ Required:
 * 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
 
@@ -27,7 +28,8 @@ Required:
 
 * 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)
 
@@ -53,6 +55,9 @@ Optional:
 * 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
 
index 0e5012d33a4f6b33eef0ac809d3007570aca90d8..4285aac2734a71c87caa4193004c3440658047c5 100644 (file)
@@ -28,10 +28,25 @@ if ( ENABLE_STATIC_DAQ )
     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()
@@ -46,16 +61,6 @@ if ( HAVE_UUID )
     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})
index af5367f0f358b12f7695efa7036719117804a58d..c51eb69e3ed0c7c87220898ddf6473880dec13f0 100644 (file)
@@ -40,6 +40,8 @@ add_library (helpers OBJECT
     process.h
     ring.h
     ring_logic.h
+    sigsafe.cc
+    sigsafe.h
     scratch_allocator.cc
 )
 
@@ -53,5 +55,11 @@ add_catch_test( base64_encoder_test
         base64_encoder.cc
 )
 
+add_catch_test( sigsafe_test
+    NO_TEST_SOURCE
+    SOURCES
+        sigsafe.cc
+)
+
 add_subdirectory(test)
 
index c5cb9f0c83a4526d1059288ca78026e187b71a4b..55f31220ff9f91071027f84cfdefd3b3c1e653a7 100644 (file)
 
 #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
 
@@ -33,6 +39,7 @@
 
 #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"
@@ -40,9 +47,9 @@
 
 #include "markup.h"
 #include "ring.h"
+#include "sigsafe.h"
 
 using namespace snort;
-using namespace std;
 
 #ifndef SIGNAL_SNORT_RELOAD
 #define SIGNAL_SNORT_RELOAD        SIGHUP
@@ -74,8 +81,38 @@ static Ring<PigSignal> sig_ring(4);
 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;
 
@@ -85,19 +122,6 @@ void set_quick_exit(bool b)
 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;
@@ -105,15 +129,15 @@ static void exit_handler(int signal)
 
     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);
     }
 
@@ -146,22 +170,114 @@ static void reload_attrib_handler(int /*signal*/)
     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);
 }
 
@@ -186,34 +302,101 @@ const char* get_signal_name(PigSignal s)
 // 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;
 }
 
@@ -225,53 +408,50 @@ void init_signals()
     // 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()
@@ -325,7 +505,7 @@ void daemonize()
     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();
 
diff --git a/src/helpers/sigsafe.cc b/src/helpers/sigsafe.cc
new file mode 100644 (file)
index 0000000..332b9dd
--- /dev/null
@@ -0,0 +1,451 @@
+//--------------------------------------------------------------------------
+// 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
+
diff --git a/src/helpers/sigsafe.h b/src/helpers/sigsafe.h
new file mode 100644 (file)
index 0000000..ab1b242
--- /dev/null
@@ -0,0 +1,46 @@
+//--------------------------------------------------------------------------
+// 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
+
index dcc89bf67a2ee163382627dce49245dfc4c98ad2..98c193b013b13a6d0908dc1003b5cd770a3838d7 100644 (file)
@@ -395,6 +395,7 @@ void Analyzer::post_process_daq_pkt_msg(Packet* p)
         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.
 
         {
@@ -417,7 +418,6 @@ void Analyzer::process_daq_pkt_msg(DAQ_Msg_h msg, bool retry)
     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);
@@ -437,13 +437,13 @@ void Analyzer::process_daq_pkt_msg(DAQ_Msg_h msg, bool retry)
         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))
     {
@@ -463,6 +463,7 @@ void Analyzer::process_daq_msg(DAQ_Msg_h msg, bool retry)
             }
             break;
     }
+    oops_handler->set_current_message(nullptr);
     {
         Profile profile(daqPerfStats);
         daq_instance->finalize_message(msg, verdict);
@@ -682,7 +683,7 @@ void Analyzer::term()
     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);
@@ -715,6 +716,7 @@ Analyzer::Analyzer(SFDAQInstance* instance, unsigned i, const char* s, uint64_t
     exit_after_cnt = msg_cnt;
     source = s ? s : "";
     daq_instance = instance;
+    oops_handler = new OopsHandler();
     retry_queue = new RetryQueue(200);
     set_state(State::NEW);
 }
@@ -728,7 +730,7 @@ Analyzer::~Analyzer()
 
 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);
@@ -761,6 +763,8 @@ void Analyzer::operator()(Swapper* ps, uint16_t run_num)
     term();
 
     set_state(State::STOPPED);
+
+    oops_handler->tterm();
 }
 
 /* Note: This will be called from the main thread.  Everything it does must be
index 890ce16526056e536fe1781092b8267d903fdf93..b270da127ac5ed5a51344def85821a0e6010dfd6 100644 (file)
 
 #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");
 }
index 61685f91550d73240dbb317217ae61be469f7e8a..a6570136e4150d8337f27adeaad1c5d5b4114a76 100644 (file)
 
 #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
index 7c3581a7603faf149ea61e26c65f467c9c6a9f3f..ac572c840c66a92aec7d96a598cd98dfadda62f8 100644 (file)
@@ -69,8 +69,8 @@ void Profiler::consolidate_stats() { }
 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) { }
index f13548656651a3c9737baa5dd33d8deb7a1458ce..0ae753917d98498b7cda81047c7420597b28883f 100644 (file)
@@ -1,4 +1,4 @@
-if ( FLATBUFFERS_FOUND )
+if ( HAVE_FLATBUFFERS )
     set( FLATBUFFERS_SOURCE fbs_formatter.h fbs_formatter.cc )
 endif()
 
@@ -39,7 +39,7 @@ else (STATIC_INSPECTORS)
 
 endif (STATIC_INSPECTORS)
 
-if ( FLATBUFFERS_FOUND )
+if ( HAVE_FLATBUFFERS )
     target_include_directories( perf_monitor PRIVATE ${FLATBUFFERS_INCLUDE_DIR} )
 endif()
 
@@ -57,7 +57,7 @@ add_catch_test( json_formatter_test
         perf_formatter.cc
 )
 
-if ( FLATBUFFERS_FOUND )
+if ( HAVE_FLATBUFFERS )
     add_catch_test( fbs_formatter_test
         NO_TEST_SOURCE
         SOURCES
index d22b039ca054e7c3829388fb51298fb319dfe3b2..a8ebc8e6fe8b61ddba936347897db8e39c7f5fce 100644 (file)
@@ -43,7 +43,7 @@ add_library ( utils OBJECT
     ${TEST_FILES}
 )
 
-if ( FLATBUFFERS_FOUND )
+if ( HAVE_FLATBUFFERS )
     target_include_directories( utils PRIVATE ${FLATBUFFERS_INCLUDE_DIR} )
 endif()
 
index 201ded1c6f4f8ee253ad0b61912808a224990caf..f0d716727c34d32097fdc73f32e53405173e0786 100644 (file)
@@ -1,4 +1,4 @@
-if ( FLATBUFFERS_FOUND )
+if ( HAVE_FLATBUFFERS )
     add_executable( fbstreamer
         fbstreamer.cc
     )