# set library variables
if (HS_FOUND)
check_library_exists (${HS_LIBRARIES} hs_scan "" HAVE_HYPERSCAN)
+ if (HAVE_HYPERSCAN)
+ cmake_push_check_state(RESET)
+ set(CMAKE_REQUIRED_INCLUDES ${HS_INCLUDE_DIRS})
+ set(CMAKE_REQUIRED_LIBRARIES ${HS_LIBRARIES})
+ check_function_exists(hs_compile_lit HAVE_HS_COMPILE_LIT)
+ cmake_pop_check_state()
+ endif()
endif()
if (DEFINED LIBLZMA_LIBRARIES)
/* hyperscan available */
#cmakedefine HAVE_HYPERSCAN 1
+#cmakedefine HAVE_HS_COMPILE_LIT 1
/* lzma available */
#cmakedefine HAVE_LZMA 1
IpsOption* opt = (IpsOption*)node->option_data;
PatternMatchData* pmd = opt->get_pattern(0, RULE_WO_DIR);
- if ( pmd and pmd->last_check )
+ if ( pmd and pmd->is_literal() and pmd->last_check )
content_last = pmd->last_check + get_instance_id();
}
IpsOption* opt = (IpsOption*)child_node->option_data;
PatternMatchData* pmd = opt->get_pattern(0, RULE_WO_DIR);
- if ( pmd and pmd->is_unbounded() )
+ if ( pmd and pmd->is_literal() and pmd->is_unbounded() )
{
// Only increment result once. Should hit this
// condition on first loop iteration
+if ( HAVE_HYPERSCAN )
+ set(HYPER_HEADERS
+ hyper_scratch_allocator.h
+ hyper_search.h
+ )
+ set(HYPER_SOURCES
+ hyper_scratch_allocator.cc
+ hyper_search.cc
+ )
+endif ()
+
set (HELPERS_INCLUDES
+ ${HYPER_HEADERS}
base64_encoder.h
+ boyer_moore_search.h
+ literal_search.h
+ scratch_allocator.h
)
add_library (helpers OBJECT
${HELPERS_INCLUDES}
+ ${HYPER_SOURCES}
base64_encoder.cc
+ boyer_moore_search.cc
chunk.cc
chunk.h
directory.cc
discovery_filter.cc
discovery_filter.h
flag_context.h
+ literal_search.cc
markup.cc
markup.h
process.cc
process.h
ring.h
ring_logic.h
+ scratch_allocator.cc
)
install (FILES ${HELPERS_INCLUDES}
SOURCES
base64_encoder.cc
)
+
+add_subdirectory(test)
+
--- /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.
+//--------------------------------------------------------------------------
+// boyer_moore_search.cc author Brandon Stultz <brastult@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cassert>
+#include <cctype>
+
+#include "boyer_moore_search.h"
+
+namespace snort
+{
+
+BoyerMooreSearch::BoyerMooreSearch(const uint8_t* pattern, unsigned pattern_len)
+ : pattern(pattern), pattern_len(pattern_len)
+{
+ assert(pattern_len > 0);
+
+ last = pattern_len - 1;
+
+ make_skip();
+}
+
+// skip[c] is the distance between the last character of the
+// pattern and the rightmost occurrence of c in the pattern.
+// If c does not occur in the pattern then skip[c] = pattern_len.
+void BoyerMooreSearch::make_skip()
+{
+ for ( unsigned i = 0; i < 256; i++ )
+ skip[i] = pattern_len;
+
+ for ( unsigned i = 0; i < last; i++ )
+ skip[pattern[i]] = last - i;
+}
+
+int BoyerMooreSearchCase::search(const uint8_t* buffer, unsigned buffer_len) const
+{
+ const uint8_t* start = buffer;
+
+ while ( buffer_len >= pattern_len )
+ {
+ for ( unsigned pos = last; buffer[pos] == pattern[pos]; pos-- )
+ if ( pos == 0 )
+ return buffer - start;
+
+ buffer_len -= skip[buffer[last]];
+ buffer += skip[buffer[last]];
+ }
+
+ return -1;
+}
+
+int BoyerMooreSearchNoCase::search(const uint8_t* buffer, unsigned buffer_len) const
+{
+ const uint8_t* start = buffer;
+
+ while ( buffer_len >= pattern_len )
+ {
+ for ( unsigned pos = last; toupper(buffer[pos]) == pattern[pos]; pos-- )
+ if ( pos == 0 )
+ return buffer - start;
+
+ buffer_len -= skip[toupper(buffer[last])];
+ buffer += skip[toupper(buffer[last])];
+ }
+
+ return -1;
+}
+
+}
+
--- /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.
+//--------------------------------------------------------------------------
+// boyer_moore_search.h author Brandon Stultz <brastult@cisco.com>
+
+#ifndef BOYER_MOORE_SEARCH_H
+#define BOYER_MOORE_SEARCH_H
+
+// Boyer-Moore literal content matching routines (single pattern)
+// use LiteralSearch::instantiate to get hyperscan if available
+
+#include "helpers/literal_search.h"
+#include "main/snort_types.h"
+
+namespace snort
+{
+
+class SO_PUBLIC BoyerMooreSearch : public LiteralSearch
+{
+protected:
+ BoyerMooreSearch(const uint8_t* pattern, unsigned pattern_len);
+
+protected:
+ void make_skip();
+
+ const uint8_t* pattern;
+ unsigned pattern_len;
+ unsigned last;
+
+ unsigned skip[256];
+};
+
+class SO_PUBLIC BoyerMooreSearchCase : public BoyerMooreSearch
+{
+public:
+ BoyerMooreSearchCase(const uint8_t* pat, unsigned pat_len) :
+ BoyerMooreSearch(pat, pat_len) { }
+
+ int search(const uint8_t* buffer, unsigned buffer_len) const;
+
+ int search(void*, const uint8_t* buffer, unsigned buffer_len) const override
+ { return search(buffer, buffer_len); }
+};
+
+class SO_PUBLIC BoyerMooreSearchNoCase : public BoyerMooreSearch
+{
+public:
+ BoyerMooreSearchNoCase(const uint8_t* pat, unsigned pat_len) :
+ BoyerMooreSearch(pat, pat_len) { }
+
+ int search(const uint8_t* buffer, unsigned buffer_len) const;
+
+ int search(void*, const uint8_t* buffer, unsigned buffer_len) const override
+ { return search(buffer, buffer_len); }
+};
+
+}
+#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.
+//--------------------------------------------------------------------------
+// hyper_scratch_allocator.cc author Russ Combs <rucombs@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "hyper_scratch_allocator.h"
+#include "log/messages.h"
+
+namespace snort
+{
+
+HyperScratchAllocator::~HyperScratchAllocator()
+{
+ if ( scratch )
+ hs_free_scratch(scratch);
+}
+
+bool HyperScratchAllocator::allocate(hs_database_t* db)
+{
+ if ( scratch_id < 0 )
+ scratch_id = SnortConfig::request_scratch(this);
+
+ return hs_alloc_scratch(db, &scratch) == HS_SUCCESS;
+}
+
+bool HyperScratchAllocator::setup(SnortConfig* sc)
+{
+ if ( !scratch )
+ return false;
+
+ for ( unsigned i = 0; i < sc->num_slots; ++i )
+ hs_clone_scratch(scratch, get_addr(sc, i));
+
+ hs_free_scratch(scratch);
+ scratch = nullptr;
+
+ return true;
+}
+
+void HyperScratchAllocator::cleanup(SnortConfig* sc)
+{
+ for ( unsigned i = 0; i < sc->num_slots; ++i )
+ {
+ hs_scratch_t* ss = get(sc, i);
+ hs_free_scratch(ss);
+ set(sc, i, nullptr);
+ }
+}
+
+}
+
--- /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.
+//--------------------------------------------------------------------------
+// hyper_scratch_allocator.h author Russ Combs <rucombs@cisco.com>
+
+#ifndef HYPER_SCRATCH_ALLOCATOR_H
+#define HYPER_SCRATCH_ALLOCATOR_H
+
+#include "helpers/scratch_allocator.h"
+
+#include <hs_compile.h>
+#include <hs_runtime.h>
+
+#include "main/snort_config.h"
+#include "main/snort_types.h"
+#include "main/thread.h"
+
+//--------------------------------------------------------------------------
+// scratch management
+//--------------------------------------------------------------------------
+
+namespace snort
+{
+struct SnortConfig;
+
+class SO_PUBLIC HyperScratchAllocator : public ScratchAllocator
+{
+public:
+ ~HyperScratchAllocator() override;
+
+ bool setup(SnortConfig*) override;
+ void cleanup(SnortConfig*) override;
+
+ bool allocate(hs_database_t*);
+
+ hs_scratch_t* get()
+ { return get(SnortConfig::get_conf(), snort::get_instance_id()); }
+
+private:
+ hs_scratch_t** get_addr(SnortConfig* sc, unsigned idx)
+ { return (hs_scratch_t**)&sc->state[idx][scratch_id]; }
+
+ hs_scratch_t* get(SnortConfig* sc, unsigned idx)
+ { return (hs_scratch_t*)sc->state[idx][scratch_id]; }
+
+ void set(SnortConfig* sc, unsigned idx, void* pv)
+ { sc->state[idx][scratch_id] = pv; }
+
+private:
+ hs_scratch_t* scratch = nullptr;
+ int scratch_id = -1;
+};
+
+}
+#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.
+//--------------------------------------------------------------------------
+// hyper_search.cc author Russ Combs <rucombs@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "hyper_search.h"
+
+#include <cassert>
+#include <cctype>
+#include <cstring>
+
+#include <hs_compile.h>
+#include <hs_runtime.h>
+
+#include "log/messages.h"
+#include "main/snort_config.h"
+#include "main/thread.h"
+
+#include "hyper_scratch_allocator.h"
+
+namespace snort
+{
+
+LiteralSearch::Handle* HyperSearch::setup()
+{ return new HyperScratchAllocator; }
+
+void HyperSearch::cleanup(LiteralSearch::Handle* h)
+{
+ HyperScratchAllocator* scratcher = (HyperScratchAllocator*)h;
+ delete scratcher;
+}
+
+//--------------------------------------------------------------------------
+
+HyperSearch::HyperSearch(LiteralSearch::Handle* h, const uint8_t* pattern, unsigned len, bool no_case)
+{
+ assert(h);
+ HyperScratchAllocator* scratcher = (HyperScratchAllocator*)h;
+
+ assert(len > 0);
+ pattern_len = len;
+
+ hs_compile_error_t* err = nullptr;
+
+ unsigned flags = HS_FLAG_SINGLEMATCH;
+ if ( no_case )
+ flags |= HS_FLAG_CASELESS;
+
+#ifndef HAVE_HS_COMPILE_LIT
+ std::string hex_pat;
+
+ for ( unsigned i = 0; i < len; ++i )
+ {
+ char hex[5];
+ snprintf(hex, sizeof(hex), "\\x%02X", pattern[i]);
+ hex_pat += hex;
+ }
+
+ if ( hs_compile((const char*)hex_pat.c_str(), flags,
+ HS_MODE_BLOCK, nullptr, (hs_database_t**)&db, &err) != HS_SUCCESS )
+#else
+ if ( hs_compile_lit((const char*)pattern, flags, pattern_len,
+ HS_MODE_BLOCK, nullptr, (hs_database_t**)&db, &err) != HS_SUCCESS )
+#endif
+ {
+ ParseError("can't compile content '%s'", pattern);
+ hs_free_compile_error(err);
+ return;
+ }
+ if ( !scratcher->allocate(db) )
+ ParseError("can't allocate scratch for content '%s'", pattern);
+}
+
+HyperSearch::~HyperSearch()
+{
+ if ( db )
+ hs_free_database(db);
+}
+
+}
+
+struct ScanContext
+{
+ unsigned index;
+ bool found = false;
+};
+
+static int hs_match(unsigned int, unsigned long long, unsigned long long to, unsigned int, void* context)
+{
+ ScanContext* scan = (ScanContext*)context;
+ scan->index = (unsigned)to;
+ scan->found = true;
+ return 1;
+}
+
+namespace snort
+{
+
+int HyperSearch::search(LiteralSearch::Handle* h, const uint8_t* buffer, unsigned buffer_len) const
+{
+ HyperScratchAllocator* scratcher = (HyperScratchAllocator*)h;
+ ScanContext scan;
+ hs_scan(db, (const char*)buffer, buffer_len, 0, scratcher->get(), hs_match, &scan);
+ return scan.found ? ((int)(scan.index - pattern_len)) : -1;
+}
+
+}
+
--- /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.
+//--------------------------------------------------------------------------
+// hyper_search.h author Russ Combs <rucombs@cisco.com>
+
+#ifndef HYPER_SEARCH_H
+#define HYPER_SEARCH_H
+
+// Hyperscan-based literal content matching (single pattern)
+// use LiteralSearch::instantiate to fallback to boyer-moore
+// if hyperscan is not available.
+
+#include "helpers/literal_search.h"
+#include "main/snort_types.h"
+
+extern "C" struct hs_database;
+
+namespace snort
+{
+
+class SO_PUBLIC HyperSearch : public snort::LiteralSearch
+{
+public:
+ using Handle = snort::LiteralSearch::Handle;
+
+ static Handle* setup(); // call from module ctor
+ static void cleanup(Handle*); // call from module dtor
+
+ HyperSearch(Handle*, const uint8_t* pattern, unsigned pattern_len, bool no_case = false);
+ ~HyperSearch() override;
+
+ int search(Handle*, const uint8_t* buffer, unsigned buffer_len) const override;
+
+private:
+ struct hs_database* db;
+ unsigned pattern_len;
+};
+
+}
+#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.
+//--------------------------------------------------------------------------
+// literal_search.cc author Russ Combs <rucombs@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "literal_search.h"
+
+#include <cstring>
+
+#include "main/snort_config.h"
+#include "boyer_moore_search.h"
+#include "hyper_search.h"
+
+namespace snort
+{
+
+// setup and cleanup for hyperscan are independent of configuration
+// because that would create a bad dependency - a module ctor needs
+// a config item from a module. also, the handle must persist for
+// for the lifetime of a module, which can span many configs.
+
+LiteralSearch::Handle* LiteralSearch::setup()
+{
+#ifdef HAVE_HYPERSCAN
+ return HyperSearch::setup();
+#else
+ return nullptr;
+#endif
+}
+
+void LiteralSearch::cleanup(LiteralSearch::Handle* h)
+{
+#ifdef HAVE_HYPERSCAN
+ HyperSearch::cleanup(h);
+#endif
+}
+
+LiteralSearch* LiteralSearch::instantiate(
+ LiteralSearch::Handle* h, const uint8_t* pattern, unsigned pattern_len, bool no_case)
+{
+#ifdef HAVE_HYPERSCAN
+ if ( SnortConfig::get_conf()->hyperscan_literals )
+ return new HyperSearch(h, pattern, pattern_len, no_case);
+#else
+ UNUSED(h);
+#endif
+ if ( no_case )
+ return new snort::BoyerMooreSearchNoCase(pattern, pattern_len);
+
+ return new snort::BoyerMooreSearchCase(pattern, pattern_len);
+}
+
+}
+
--- /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.
+//--------------------------------------------------------------------------
+// literal_search.h author Russ Combs <rucombs@cisco.com>
+
+#ifndef LITERAL_SEARCH_H
+#define LITERAL_SEARCH_H
+
+// literal content matching (single pattern)
+// used eg with content during signature evaluation
+
+#include "main/snort_types.h"
+
+namespace snort
+{
+
+class SO_PUBLIC LiteralSearch
+{
+public:
+ using Handle = void;
+
+ static Handle* setup(); // call from module ctor
+ static void cleanup(Handle*); // call from module dtor
+
+ static LiteralSearch* instantiate(Handle*, const uint8_t* pattern, unsigned pattern_len, bool no_case = false);
+ virtual ~LiteralSearch() { }
+
+ virtual int search(Handle*, const uint8_t* buffer, unsigned buffer_len) const = 0;
+
+protected:
+ LiteralSearch() { }
+};
+
+}
+#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.
+//--------------------------------------------------------------------------
+// scratch_allocator.cc author Russ Combs <rucombs@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "scratch_allocator.h"
+#include "main/snort_config.h"
+
+namespace snort
+{
+
+ScratchAllocator::ScratchAllocator()
+{ id = SnortConfig::request_scratch(this); }
+
+ScratchAllocator::~ScratchAllocator()
+{ SnortConfig::release_scratch(id); }
+
+}
+
--- /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.
+//--------------------------------------------------------------------------
+// scratch_allocator.h author Russ Combs <rucombs@cisco.com>
+
+#ifndef SCRATCH_ALLOCATOR_H
+#define SCRATCH_ALLOCATOR_H
+
+// manages scratch memory - allocates required memory for each packet thread
+// in SnortConfig.state[slot][id] where 0 <= slot < SnortConfig.num_slots and
+// id = SnortConfig::request_scratch(). The use of scratch memory is strictly
+// per packet, it can not be referenced on a flow, as it will change with each
+// config reload.
+//
+// setup() should return false if no memory was allocated otherwise cleanup()
+// will be called when the config is deleted. this can happen eg if the
+// associated module is not used in the current configuration.
+//
+// scratch allocators may use a prototype to allocate the packet thread
+// memory. the prototype should be freed in setup to avoid leaks and to
+// ensure the prototypes for different configs are not interdependent (eg
+// preventing a decrease in required scratch).
+
+#include "main/snort_types.h"
+
+namespace snort
+{
+struct SnortConfig;
+
+class SO_PUBLIC ScratchAllocator
+{
+public:
+ virtual ~ScratchAllocator();
+
+ virtual bool setup(SnortConfig*) = 0;
+ virtual void cleanup(SnortConfig*) = 0;
+
+ int get_id() { return id; }
+
+protected:
+ ScratchAllocator();
+
+private:
+ int id;
+};
+
+typedef bool (* ScratchSetup)(SnortConfig*);
+typedef void (* ScratchCleanup)(SnortConfig*);
+
+class SO_PUBLIC SimpleScratchAllocator : public ScratchAllocator
+{
+public:
+ SimpleScratchAllocator(ScratchSetup fs, ScratchCleanup fc) : fsetup(fs), fcleanup(fc) { }
+
+ bool setup(SnortConfig* sc) override
+ { return fsetup(sc); }
+
+ void cleanup(SnortConfig* sc) override
+ { fcleanup(sc); }
+
+private:
+ ScratchSetup fsetup;
+ ScratchCleanup fcleanup;
+};
+
+}
+#endif
+
--- /dev/null
+add_cpputest( boyer_moore_search_test
+ SOURCES
+ ../boyer_moore_search.cc
+)
+
+if ( HAVE_HYPERSCAN )
+ add_cpputest( hyper_search_test
+ SOURCES
+ ../hyper_search.cc
+ ../../helpers/scratch_allocator.cc
+ ../../helpers/hyper_scratch_allocator.cc
+ LIBS
+ ${HS_LIBRARIES}
+ )
+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.
+//--------------------------------------------------------------------------
+// boyer_moore_search_test.cc author Brandon Stultz <brastult@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "../boyer_moore_search.h"
+
+#include <algorithm>
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTestExt/MockSupport.h>
+#include <CppUTest/TestHarness.h>
+#include <string>
+
+using namespace std;
+using namespace snort;
+
+enum TestType
+{
+ CASE,
+ NOCASE,
+};
+
+class Tester
+{
+public:
+ Tester(const char* pat_str, const char* buf_str, TestType typ, int idx)
+ : pat(pat_str), buf(buf_str), type(typ), index(idx)
+ {
+ if ( type == NOCASE )
+ transform(pat.begin(), pat.end(), pat.begin(), ::toupper);
+
+ pattern_len = pat.length();
+ buffer_len = buf.length();
+
+ pattern = (const uint8_t*)(pat.c_str());
+ buffer = (const uint8_t*)(buf.c_str());
+ }
+
+ bool run();
+
+private:
+ string pat;
+ string buf;
+
+ TestType type;
+
+ int index;
+
+ unsigned pattern_len;
+ unsigned buffer_len;
+
+ const uint8_t* pattern;
+ const uint8_t* buffer;
+};
+
+bool Tester::run()
+{
+ int pos;
+
+ if ( type == NOCASE )
+ {
+ BoyerMooreSearchNoCase bm = BoyerMooreSearchNoCase(pattern, pattern_len);
+ pos = bm.search(buffer, buffer_len);
+ }
+ else
+ {
+ BoyerMooreSearchCase bm = BoyerMooreSearchCase(pattern, pattern_len);
+ pos = bm.search(buffer, buffer_len);
+ }
+
+ return pos == index;
+}
+
+TEST_GROUP(boyer_moore_test_group) {};
+
+TEST(boyer_moore_test_group, binary)
+{
+ const uint8_t pat[] = { 0xCA, 0xFE, 0xBA, 0xBE };
+
+ const uint8_t buf[] = {
+ 0x00, 0x01, 0x02, 0x03,
+ 0x72, 0x01, 0x3F, 0x2B,
+ 0x1F, 0xCA, 0xFE, 0xBA,
+ 0xBE, 0x01, 0x02, 0x03,
+ };
+
+ BoyerMooreSearchCase bm = BoyerMooreSearchCase(pat, sizeof(pat));
+
+ int pos = bm.search(buf, sizeof(buf));
+
+ CHECK(pos == 9);
+}
+
+TEST(boyer_moore_test_group, empty)
+{
+ Tester t = Tester("abc", "", CASE, -1);
+ CHECK(t.run());
+}
+
+TEST(boyer_moore_test_group, start)
+{
+ Tester t = Tester("abc", "abc", CASE, 0);
+ CHECK(t.run());
+}
+
+TEST(boyer_moore_test_group, start_nocase)
+{
+ Tester t = Tester("abc", "aBc", NOCASE, 0);
+ CHECK(t.run());
+}
+
+TEST(boyer_moore_test_group, found1)
+{
+ Tester t = Tester("d", "abcdefg", CASE, 3);
+ CHECK(t.run());
+}
+
+TEST(boyer_moore_test_group, found2)
+{
+ Tester t = Tester("nan", "banana", CASE, 2);
+ CHECK(t.run());
+}
+
+TEST(boyer_moore_test_group, found3)
+{
+ Tester t = Tester("pan", "anpanman", CASE, 2);
+ CHECK(t.run());
+}
+
+TEST(boyer_moore_test_group, found4)
+{
+ Tester t = Tester("bcd", "abcd", CASE, 1);
+ CHECK(t.run());
+}
+
+TEST(boyer_moore_test_group, found5)
+{
+ Tester t = Tester("aa", "aaa", CASE, 0);
+ CHECK(t.run());
+}
+
+TEST(boyer_moore_test_group, found6)
+{
+ Tester t = Tester(
+ "that", "which finally halts at tHaT point", NOCASE, 23);
+ CHECK(t.run());
+}
+
+TEST(boyer_moore_test_group, not_found1)
+{
+ Tester t = Tester("nnaaman", "anpanmanam", CASE, -1);
+ CHECK(t.run());
+}
+
+TEST(boyer_moore_test_group, not_found2)
+{
+ Tester t = Tester("abcd", "abc", CASE, -1);
+ CHECK(t.run());
+}
+
+TEST(boyer_moore_test_group, not_found3)
+{
+ Tester t = Tester("abcd", "bcd", CASE, -1);
+ CHECK(t.run());
+}
+
+TEST(boyer_moore_test_group, not_found4)
+{
+ Tester t = Tester("baa", "aaaaa", CASE, -1);
+ CHECK(t.run());
+}
+
+int main(int argc, char** argv)
+{
+ return CommandLineTestRunner::RunAllTests(argc, argv);
+}
+
--- /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.
+//--------------------------------------------------------------------------
+// hyper_search_test.cc author Russ Combs <rucombs@cisco.com>
+// with tests ripped from boyer_moore_search_test.cc
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "../hyper_search.h"
+
+#include "main/snort_config.h"
+
+#include <algorithm>
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTestExt/MockSupport.h>
+#include <CppUTest/TestHarness.h>
+#include <string>
+
+using namespace std;
+using namespace snort;
+
+//-------------------------------------------------------------------------
+// mock snort config scratch handling
+//-------------------------------------------------------------------------
+
+namespace snort
+{
+
+SnortConfig s_conf;
+THREAD_LOCAL SnortConfig* snort_conf = &s_conf;
+
+static std::vector<void *> s_state;
+static ScratchAllocator* scratcher = nullptr;
+
+static unsigned s_parse_errors = 0;
+
+SnortConfig::SnortConfig(const SnortConfig* const)
+{
+ state = &s_state;
+ num_slots = 1;
+}
+
+SnortConfig::~SnortConfig() = default;
+
+int SnortConfig::request_scratch(ScratchAllocator* s)
+{
+ scratcher = s;
+ s_state.resize(1);
+ return 0;
+}
+
+void SnortConfig::release_scratch(int)
+{ s_state.clear(); }
+
+SnortConfig* SnortConfig::get_conf()
+{ return snort_conf; }
+
+void ParseError(const char*, ...)
+{ ++s_parse_errors; }
+
+unsigned get_instance_id()
+{ return 0; }
+
+}
+
+//-------------------------------------------------------------------------
+// tests
+//-------------------------------------------------------------------------
+
+enum TestType
+{
+ CASE,
+ NOCASE,
+};
+
+class Tester
+{
+public:
+ Tester(const char* pat_str, const char* buf_str, TestType typ, int idx)
+ : pat(pat_str), buf(buf_str), type(typ), index(idx)
+ {
+ if ( type == NOCASE )
+ transform(pat.begin(), pat.end(), pat.begin(), ::toupper);
+
+ pattern_len = pat.length();
+ buffer_len = buf.length();
+
+ pattern = (const uint8_t*)(pat.c_str());
+ buffer = (const uint8_t*)(buf.c_str());
+ }
+
+ bool run(HyperSearch::Handle*);
+
+private:
+ string pat;
+ string buf;
+
+ TestType type;
+
+ int index;
+
+ unsigned pattern_len;
+ unsigned buffer_len;
+
+ const uint8_t* pattern;
+ const uint8_t* buffer;
+};
+
+bool Tester::run(HyperSearch::Handle* handle)
+{
+ HyperSearch hs(handle, pattern, pattern_len, (type == NOCASE));
+ bool do_cleanup = scratcher->setup(snort_conf);
+ int pos = hs.search(handle, buffer, buffer_len);
+
+ if ( do_cleanup )
+ scratcher->cleanup(snort_conf);
+
+ return pos == index;
+}
+
+TEST_GROUP(hyper_search_test_group)
+{
+ HyperSearch::Handle* handle = nullptr;
+
+ void setup() override
+ {
+ s_parse_errors = 0;
+ handle = HyperSearch::setup();
+ }
+
+ void teardown() override
+ {
+ HyperSearch::cleanup(handle);
+ CHECK(s_parse_errors == 0);
+ }
+};
+
+TEST(hyper_search_test_group, binary)
+{
+ const uint8_t pat[] = { 0xCA, 0xFE, 0xBA, 0xBE, 0x00 };
+
+ const uint8_t buf[] = {
+ 0x00, 0x01, 0x02, 0x03,
+ 0x72, 0x01, 0x3F, 0x2B,
+ 0x1F, 0xCA, 0xFE, 0xBA,
+ 0xBE, 0x01, 0x02, 0x03,
+ };
+
+ HyperSearch hs(handle, pat, sizeof(pat)-1, true);
+ bool do_cleanup = scratcher->setup(snort_conf);
+ int pos = hs.search(handle, buf, sizeof(buf));
+
+ if ( do_cleanup )
+ scratcher->cleanup(snort_conf);
+
+ CHECK(pos == 9);
+}
+
+TEST(hyper_search_test_group, empty)
+{
+ Tester t = Tester("abc", "", CASE, -1);
+ CHECK(t.run(handle));
+}
+
+TEST(hyper_search_test_group, start)
+{
+ Tester t = Tester("abc", "abc", CASE, 0);
+ CHECK(t.run(handle));
+}
+
+TEST(hyper_search_test_group, start_nocase)
+{
+ Tester t = Tester("abc", "aBc", NOCASE, 0);
+ CHECK(t.run(handle));
+}
+
+TEST(hyper_search_test_group, found1)
+{
+ Tester t = Tester("d", "abcdefg", CASE, 3);
+ CHECK(t.run(handle));
+}
+
+TEST(hyper_search_test_group, found2)
+{
+ Tester t = Tester("nan", "banana", CASE, 2);
+ CHECK(t.run(handle));
+}
+
+TEST(hyper_search_test_group, found3)
+{
+ Tester t = Tester("pan", "anpanman", CASE, 2);
+ CHECK(t.run(handle));
+}
+
+TEST(hyper_search_test_group, found4)
+{
+ Tester t = Tester("bcd", "abcd", CASE, 1);
+ CHECK(t.run(handle));
+}
+
+TEST(hyper_search_test_group, found5)
+{
+ Tester t = Tester("aa", "aaa", CASE, 0);
+ CHECK(t.run(handle));
+}
+
+TEST(hyper_search_test_group, found6)
+{
+ Tester t = Tester(
+ "that", "which finally halts at tHaT point", NOCASE, 23);
+ CHECK(t.run(handle));
+}
+
+TEST(hyper_search_test_group, not_found1)
+{
+ Tester t = Tester("nnaaman", "anpanmanam", CASE, -1);
+ CHECK(t.run(handle));
+}
+
+TEST(hyper_search_test_group, not_found2)
+{
+ Tester t = Tester("abcd", "abc", CASE, -1);
+ CHECK(t.run(handle));
+}
+
+TEST(hyper_search_test_group, not_found3)
+{
+ Tester t = Tester("abcd", "bcd", CASE, -1);
+ CHECK(t.run(handle));
+}
+
+TEST(hyper_search_test_group, not_found4)
+{
+ Tester t = Tester("baa", "aaaaa", CASE, -1);
+ CHECK(t.run(handle));
+}
+
+TEST(hyper_search_test_group, compiler_error)
+{
+ HyperSearch hs(handle, nullptr, 1);
+ CHECK(s_parse_errors == 1);
+ s_parse_errors = 0;
+}
+
+int main(int argc, char** argv)
+{
+ MemoryLeakWarningPlugin::turnOffNewDeleteOverloads();
+ return CommandLineTestRunner::RunAllTests(argc, argv);
+}
+
#include "framework/ips_option.h"
#include "framework/module.h"
#include "hash/hashfcn.h"
+#include "helpers/literal_search.h"
#include "log/messages.h"
#include "parser/parse_utils.h"
#include "profiler/profiler.h"
-#include "utils/boyer_moore.h"
#include "utils/util.h"
#include "utils/stats.h"
#define s_name "content"
static THREAD_LOCAL ProfileStats contentPerfStats;
+static LiteralSearch::Handle* search_handle = nullptr;
static IpsOption::EvalStatus CheckANDPatternMatch(class ContentData*, Cursor&);
{
public:
ContentData();
-
~ContentData();
void setup_bm();
PatternMatchData pmd = {};
- BoyerMoore* boyer_moore;
+ LiteralSearch* searcher;
int8_t offset_var; /* byte_extract variable indices for offset, */
int8_t depth_var; /* depth, distance, within */
ContentData::ContentData()
{
- boyer_moore = nullptr;
+ searcher = nullptr;
offset_var = IPS_OPTIONS_NO_VAR;
depth_var = IPS_OPTIONS_NO_VAR;
match_delta = 0;
ContentData::~ContentData()
{
- if ( boyer_moore )
- delete boyer_moore;
+ if ( searcher )
+ delete searcher;
if ( pmd.pattern_buf )
snort_free(const_cast<char*>(pmd.pattern_buf));
void ContentData::setup_bm()
{
const uint8_t* pattern = (const uint8_t*)pmd.pattern_buf;
-
- boyer_moore = new BoyerMoore(pattern, pmd.pattern_size);
+ searcher = LiteralSearch::instantiate(search_handle, pattern, pmd.pattern_size, pmd.is_no_case());
}
// find the maximum number of characters we can jump ahead
}
const uint8_t* base = c.buffer() + pos;
- int found;
-
- if ( cd->pmd.is_no_case() )
- {
- found = cd->boyer_moore->search_nocase(base, depth);
- }
- else
- {
- found = cd->boyer_moore->search(base, depth);
- }
+ int found = cd->searcher->search(search_handle, base, depth);
if ( found >= 0 )
{
{
public:
ContentModule() : Module(s_name, s_help, s_params)
- { cd = nullptr; }
+ {
+ cd = nullptr;
+ search_handle = LiteralSearch::setup();
+ }
~ContentModule() override
- { delete cd; }
+ {
+ delete cd;
+ LiteralSearch::cleanup(search_handle);
+ }
bool begin(const char*, int, SnortConfig*) override;
bool end(const char*, int, SnortConfig*) override;
#include "framework/ips_option.h"
#include "framework/module.h"
#include "hash/hashfcn.h"
+#include "helpers/scratch_allocator.h"
#include "log/messages.h"
#include "main/snort_config.h"
#include "profiler/profiler.h"
char* expression;
};
-/*
- * we need to specify the vector length for our pcre_exec call. we only care
- * about the first vector, which if the match is successful will include the
- * offset to the end of the full pattern match. If we decide to store other
- * matches, make *SURE* that this is a multiple of 3 as pcre requires it.
- */
-// the wrong size caused the pcre lib to segfault but that has since been
-// fixed. it may be that with the updated lib, the need to get the size
-// exactly correct is obviated and thus the need to reload as well.
-
-/* Since SO rules are loaded 1 time at startup, regardless of
- * configuration, we won't pcre_capture count again, so save the max. */
-static int s_ovector_max = 0;
+// we need to specify the vector length for our pcre_exec call. we only care
+// about the first vector, which if the match is successful will include the
+// offset to the end of the full pattern match. if we decide to store other
+// matches, make *SURE* that this is a multiple of 3 as pcre requires it.
// this is a temporary value used during parsing and set in snort conf
// by verify; search uses the value in snort conf
-static int s_ovector_size = 0;
+static int s_ovector_max = -1;
static unsigned scratch_index;
+static ScratchAllocator* scratcher = nullptr;
static THREAD_LOCAL ProfileStats pcrePerfStats;
pcre_fullinfo((const pcre*)code, (const pcre_extra*)extra,
PCRE_INFO_CAPTURECOUNT, &tmp_ovector_size);
- if (tmp_ovector_size > s_ovector_size)
- s_ovector_size = tmp_ovector_size;
+ if (tmp_ovector_size > s_ovector_max)
+ s_ovector_max = tmp_ovector_size;
}
static void pcre_check_anchored(PcreData* pcre_data)
}
}
-static void pcre_parse(const char* data, PcreData* pcre_data)
+static void pcre_parse(const SnortConfig* sc, const char* data, PcreData* pcre_data)
{
const char* error;
char* re, * free_me;
* these are snort specific don't work with pcre or perl
*/
case 'R': pcre_data->options |= SNORT_PCRE_RELATIVE; break;
- case 'O': pcre_data->options |= SNORT_OVERRIDE_MATCH_LIMIT; break;
+ case 'O':
+ if ( sc->pcre_override )
+ pcre_data->options |= SNORT_OVERRIDE_MATCH_LIMIT;
+ break;
default:
ParseError("unknown/extra pcre option encountered");
PcreModule() : Module(s_name, s_help, s_params)
{
data = nullptr;
- scratch_index = SnortConfig::request_scratch(
- PcreModule::scratch_setup, PcreModule::scratch_cleanup);
+ scratcher = new SimpleScratchAllocator(scratch_setup, scratch_cleanup);
+ scratch_index = scratcher->get_id();
}
~PcreModule() override
- { delete data; }
+ {
+ delete data;
+ delete scratcher;
+ }
bool begin(const char*, int, SnortConfig*) override;
bool set(const char*, Value&, SnortConfig*) override;
private:
PcreData* data;
- static void scratch_setup(SnortConfig* sc);
+ static bool scratch_setup(SnortConfig* sc);
static void scratch_cleanup(SnortConfig* sc);
};
return true;
}
-bool PcreModule::set(const char*, Value& v, SnortConfig*)
+bool PcreModule::set(const char*, Value& v, SnortConfig* sc)
{
if ( v.is("~re") )
- pcre_parse(v.get_string(), data);
+ pcre_parse(sc, v.get_string(), data);
else
return false;
return true;
}
-void PcreModule::scratch_setup(SnortConfig* sc)
+bool PcreModule::scratch_setup(SnortConfig* sc)
{
+ if ( s_ovector_max < 0 )
+ return false;
+
+ // The pcre_fullinfo() function can be used to find out how many
+ // capturing subpatterns there are in a compiled pattern. The
+ // smallest size for ovector that will allow for n captured
+ // substrings, in addition to the offsets of the substring matched
+ // by the whole pattern is 3(n+1).
+
+ sc->pcre_ovector_size = 3 * (s_ovector_max + 1);
+ s_ovector_max = -1;
+
for ( unsigned i = 0; i < sc->num_slots; ++i )
{
std::vector<void *>& ss = sc->state[i];
- ss[scratch_index] = snort_calloc(s_ovector_max, sizeof(int));
+ ss[scratch_index] = snort_calloc(sc->pcre_ovector_size, sizeof(int));
}
+ return true;
}
void PcreModule::scratch_cleanup(SnortConfig* sc)
for ( unsigned i = 0; i < sc->num_slots; ++i )
{
std::vector<void *>& ss = sc->state[i];
-
- if ( ss[scratch_index] )
- snort_free(ss[scratch_index]);
-
+ snort_free(ss[scratch_index]);
ss[scratch_index] = nullptr;
}
}
delete p;
}
-static void pcre_verify(SnortConfig* sc)
-{
- /* The pcre_fullinfo() function can be used to find out how many
- * capturing subpatterns there are in a compiled pattern. The
- * smallest size for ovector that will allow for n captured
- * substrings, in addition to the offsets of the substring matched
- * by the whole pattern, is (n+1)*3. */
- s_ovector_size += 1;
- s_ovector_size *= 3;
-
- if (s_ovector_size > s_ovector_max)
- s_ovector_max = s_ovector_size;
-
- sc->pcre_ovector_size = s_ovector_size;
- s_ovector_size = 0;
-}
-
static const IpsApi pcre_api =
{
{
nullptr,
pcre_ctor,
pcre_dtor,
- pcre_verify
+ nullptr
};
#ifdef BUILDING_SO
#include "framework/ips_option.h"
#include "framework/module.h"
#include "hash/hashfcn.h"
+#include "helpers/hyper_scratch_allocator.h"
#include "log/messages.h"
#include "main/snort_config.h"
#include "profiler/profiler.h"
}
};
-// we need to update scratch in the main thread as each pattern is processed
-// and then clone to thread specific after all rules are loaded. s_scratch is
-// a prototype that is large enough for all uses.
-
-// FIXIT-L s_scratch persists for the lifetime of the program. it is
-// modeled off 2X where, due to so rule processing at startup, it is necessary
-// to monotonically grow the ovector. however, we should be able to free
-// s_scratch after cloning since so rules are now parsed the same as text
-// rules.
-
-static hs_scratch_t* s_scratch = nullptr;
-static unsigned scratch_index;
-static THREAD_LOCAL unsigned s_to = 0;
+static HyperScratchAllocator* scratcher = nullptr;
static THREAD_LOCAL ProfileStats regex_perf_stats;
//-------------------------------------------------------------------------
{
config = c;
- if ( /*hs_error_t err =*/ hs_alloc_scratch(config.db, &s_scratch) )
- {
- // FIXIT-RC why is this failing but everything is working?
- //ParseError("can't initialize regex for '%s' (%d) %p",
- // config.re.c_str(), err, s_scratch);
- }
+ if ( !scratcher->allocate(config.db) )
+ ParseError("can't allocate scratch for regex '%s'", config.re.c_str());
+
config.pmd.pattern_buf = config.re.c_str();
config.pmd.pattern_size = config.re.size();
return this == &ips;
}
+struct ScanContext
+{
+ unsigned index;
+ bool found = false;
+};
+
static int hs_match(
unsigned int /*id*/, unsigned long long /*from*/, unsigned long long to,
- unsigned int /*flags*/, void* /*context*/)
+ unsigned int /*flags*/, void* context)
{
- s_to = (unsigned)to;
- return 1; // stop search
+ ScanContext* scan = (ScanContext*)context;
+ scan->index = (unsigned)to;
+ scan->found = true;
+ return 1;
}
IpsOption::EvalStatus RegexOption::eval(Cursor& c, Packet*)
if ( pos > c.size() )
return NO_MATCH;
- hs_scratch_t* ss =
- (hs_scratch_t*)SnortConfig::get_conf()->state[get_instance_id()][scratch_index];
-
- s_to = 0;
+ ScanContext scan;
hs_error_t stat = hs_scan(
config.db, (const char*)c.buffer()+pos, c.size()-pos, 0,
- ss, hs_match, nullptr);
+ scratcher->get(), hs_match, &scan);
- if ( s_to and stat == HS_SCAN_TERMINATED )
+ if ( scan.found and stat == HS_SCAN_TERMINATED )
{
- s_to += pos;
- c.set_pos(s_to);
- c.set_delta(s_to);
+ scan.index += pos;
+ c.set_pos(scan.index);
+ c.set_delta(scan.index);
return MATCH;
}
return NO_MATCH;
{
public:
RegexModule() : Module(s_name, s_help, s_params)
- {
- scratch_index = SnortConfig::request_scratch(
- RegexModule::scratch_setup, RegexModule::scratch_cleanup);
- }
+ { scratcher = new HyperScratchAllocator; }
+
~RegexModule() override;
bool begin(const char*, int, SnortConfig*) override;
private:
RegexConfig config;
- static void scratch_setup(SnortConfig* sc);
- static void scratch_cleanup(SnortConfig* sc);
};
RegexModule::~RegexModule()
{
if ( config.db )
hs_free_database(config.db);
+
+ delete scratcher;
}
bool RegexModule::begin(const char*, int, SnortConfig*)
return true;
}
-void RegexModule::scratch_setup(SnortConfig* sc)
-{
- for ( unsigned i = 0; i < sc->num_slots; ++i )
- {
- hs_scratch_t** ss = (hs_scratch_t**) &sc->state[i][scratch_index];
-
- if ( s_scratch )
- hs_clone_scratch(s_scratch, ss);
- else
- ss = nullptr;
- }
-}
-
-void RegexModule::scratch_cleanup(SnortConfig* sc)
-{
- for ( unsigned i = 0; i < sc->num_slots; ++i )
- {
- hs_scratch_t* ss = (hs_scratch_t*) sc->state[i][scratch_index];
-
- if ( ss )
- {
- hs_free_scratch(ss);
- ss = nullptr;
- }
- }
-}
-
//-------------------------------------------------------------------------
// api methods
//-------------------------------------------------------------------------
static void regex_dtor(IpsOption* p)
{ delete p; }
-static void regex_pterm(SnortConfig*)
-{
- if ( s_scratch )
- hs_free_scratch(s_scratch);
-
- s_scratch = nullptr;
-}
-
static const IpsApi regex_api =
{
{
OPT_TYPE_DETECTION,
0, 0,
nullptr,
- regex_pterm,
+ nullptr,
nullptr,
nullptr,
regex_ctor,
#include "framework/ips_option.h"
#include "framework/module.h"
#include "hash/hashfcn.h"
+#include "helpers/hyper_scratch_allocator.h"
#include "log/messages.h"
#include "log/obfuscator.h"
#include "main/snort_config.h"
#define SD_SOCIAL_NODASHES_PATTERN R"(\d{9})"
#define SD_CREDIT_PATTERN_ALL R"(\d{4}\D?\d{4}\D?\d{2}\D?\d{2}\D?\d{3,4})"
-// we need to update scratch in the main thread as each pattern is processed
-// and then clone to thread specific after all rules are loaded. s_scratch is
-// a prototype that is large enough for all uses.
-
-static hs_scratch_t* s_scratch = nullptr;
-static unsigned scratch_index;
+static HyperScratchAllocator* scratcher = nullptr;
struct SdStats
{
SdPatternOption::SdPatternOption(const SdPatternConfig& c) :
IpsOption(s_name, RULE_OPTION_TYPE_BUFFER_USE), config(c)
{
- if ( hs_error_t err = hs_alloc_scratch(config.db, &s_scratch) )
- {
- // FIXIT-L why is this failing but everything is working?
- ParseError("can't initialize sd_pattern for %s (%d) %p",
- config.pii.c_str(), err, (void*)s_scratch);
- }
+ if ( !scratcher->allocate(config.db) )
+ ParseError("can't allocate scratch for sd_pattern '%s'", config.pii.c_str());
config.pmd.pattern_buf = config.pii.c_str();
config.pmd.pattern_size = config.pii.size();
const uint8_t* buf = c.start();
unsigned int buflen = c.length();
- std::vector<void *> ss = SnortConfig::get_conf()->state[get_instance_id()];
- assert(ss[scratch_index]);
-
hsContext ctx(config, p, start, buf, buflen);
hs_error_t stat = hs_scan(config.db, (const char*)buf, buflen, 0,
- (hs_scratch_t*)ss[scratch_index], hs_match, (void*)&ctx);
+ scratcher->get(), hs_match, (void*)&ctx);
if ( stat == HS_SCAN_TERMINATED )
++s_stats.terminated;
{
public:
SdPatternModule() : Module(s_name, s_help, s_params)
- {
- scratch_index = SnortConfig::request_scratch(
- SdPatternModule::scratch_setup, SdPatternModule::scratch_cleanup);
- }
+ { scratcher = new HyperScratchAllocator; }
+
+ ~SdPatternModule() override
+ { delete scratcher; }
bool begin(const char*, int, SnortConfig*) override;
bool set(const char*, Value& v, SnortConfig*) override;
private:
SdPatternConfig config;
-
- static void scratch_setup(SnortConfig*);
- static void scratch_cleanup(SnortConfig*);
};
bool SdPatternModule::begin(const char*, int, SnortConfig*)
return true;
}
-//-------------------------------------------------------------------------
-// public methods
-//-------------------------------------------------------------------------
-
-void SdPatternModule::scratch_setup(SnortConfig* sc)
-{
- for ( unsigned i = 0; i < sc->num_slots; ++i )
- {
- std::vector<void *>& ss = sc->state[i];
-
- if ( s_scratch )
- hs_clone_scratch(s_scratch, (hs_scratch_t**)&ss[scratch_index]);
- else
- ss[scratch_index] = nullptr;
- }
-}
-
-void SdPatternModule::scratch_cleanup(SnortConfig* sc)
-{
- for ( unsigned i = 0; i < sc->num_slots; ++i )
- {
- std::vector<void *>& ss = sc->state[i];
-
- if ( ss[scratch_index] )
- {
- hs_free_scratch((hs_scratch_t*)ss[scratch_index]);
- ss[scratch_index] = nullptr;
- }
- }
-}
-
//-------------------------------------------------------------------------
// api methods
//-------------------------------------------------------------------------
../../framework/module.cc
../../framework/ips_option.cc
../../framework/value.cc
+ ../../helpers/scratch_allocator.cc
+ ../../helpers/hyper_scratch_allocator.cc
../../sfip/sf_ip.cc
$<TARGET_OBJECTS:catch_tests>
LIBS
THREAD_LOCAL SnortConfig* snort_conf = &s_conf;
static std::vector<void *> s_state;
-
-ScScratchFunc scratch_setup;
-ScScratchFunc scratch_cleanup;
+static ScratchAllocator* scratcher = nullptr;
SnortConfig::SnortConfig(const SnortConfig* const)
{
SnortConfig::~SnortConfig() = default;
-int SnortConfig::request_scratch(ScScratchFunc setup, ScScratchFunc cleanup)
+int SnortConfig::request_scratch(ScratchAllocator* s)
{
- scratch_setup = setup;
- scratch_cleanup = cleanup;
+ scratcher = s;
s_state.resize(1);
-
return 0;
}
+void SnortConfig::release_scratch(int) { }
+
SnortConfig* SnortConfig::get_conf()
{ return snort_conf; }
return nullptr;
}
-static IpsOption* get_option(const char* pat, bool relative = false)
+static IpsOption* get_option(Module* mod, const char* pat, bool relative = false)
{
- Module* mod = ips_regex->mod_ctor();
mod->begin(ips_regex->name, 0, nullptr);
Value vs(pat);
IpsApi* api = (IpsApi*)ips_regex;
IpsOption* opt = api->ctor(mod, nullptr);
- ips_regex->mod_dtor(mod);
return opt;
}
TEST_GROUP(ips_regex_option)
{
+ Module* mod = nullptr;
IpsOption* opt = nullptr;
+ bool do_cleanup = false;
void setup() override
{
- opt = get_option(" foo ");
- scratch_setup(snort_conf);
+ mod = ips_regex->mod_ctor();
+ opt = get_option(mod, " foo ");
}
void teardown() override
{
IpsApi* api = (IpsApi*)ips_regex;
api->dtor(opt);
- scratch_cleanup(snort_conf);
- api->pterm(snort_conf);
+ if ( do_cleanup )
+ scratcher->cleanup(snort_conf);
+ ips_regex->mod_dtor(mod);
}
};
TEST(ips_regex_option, hash)
{
- IpsOption* opt2 = get_option("bar");
+ IpsOption* opt2 = get_option(mod, "bar");
CHECK(opt2);
CHECK(*opt != *opt2);
uint32_t h2 = opt2->hash();
CHECK(h1 != h2);
+ do_cleanup = scratcher->setup(snort_conf);
+
IpsApi* api = (IpsApi*)ips_regex;
api->dtor(opt2);
}
TEST(ips_regex_option, opeq)
{
- IpsOption* opt2 = get_option(" foo ");
+ IpsOption* opt2 = get_option(mod, " foo ");
CHECK(opt2);
// this is forced unequal for now
CHECK(*opt != *opt2);
+ do_cleanup = scratcher->setup(snort_conf);
+
IpsApi* api = (IpsApi*)ips_regex;
api->dtor(opt2);
}
TEST(ips_regex_option, match_absolute)
{
+ do_cleanup = scratcher->setup(snort_conf);
+
Packet pkt;
pkt.data = (uint8_t*)"* foo stew *";
pkt.dsize = strlen((char*)pkt.data);
TEST(ips_regex_option, no_match_delta)
{
+ do_cleanup = scratcher->setup(snort_conf);
+
Packet pkt;
pkt.data = (uint8_t*)"* foo stew *";
pkt.dsize = strlen((char*)pkt.data);
TEST_GROUP(ips_regex_option_relative)
{
+ Module* mod = nullptr;
IpsOption* opt = nullptr;
+ bool do_cleanup = false;
void setup() override
{
- opt = get_option("\\bfoo", true);
- scratch_setup(snort_conf);
+ mod = ips_regex->mod_ctor();
+ opt = get_option(mod, "\\bfoo", true);
}
void teardown() override
{
IpsApi* api = (IpsApi*)ips_regex;
api->dtor(opt);
- scratch_cleanup(snort_conf);
+ if ( do_cleanup )
+ scratcher->cleanup(snort_conf);
+ ips_regex->mod_dtor(mod);
}
};
TEST(ips_regex_option_relative, no_match)
{
+ do_cleanup = scratcher->setup(snort_conf);
+
Packet pkt;
pkt.data = (uint8_t*)"* foo stew *";
pkt.dsize = strlen((char*)pkt.data);
{ "global_rule_state", Parameter::PT_BOOL, nullptr, "false",
"apply rule_state against all policies" },
+#ifdef HAVE_HYPERSCAN
+ { "hyperscan_literals", Parameter::PT_BOOL, nullptr, "false",
+ "use hyperscan for content literal searches instead of boyer-moore" },
+#endif
+
{ "offload_limit", Parameter::PT_INT, "0:max32", "99999",
"minimum sizeof PDU to offload fast pattern search (defaults to disabled)" },
"maximum number of simultaneous offloads (defaults to disabled)" },
{ "pcre_enable", Parameter::PT_BOOL, nullptr, "true",
- "disable pcre pattern matching" },
+ "enable pcre pattern matching" },
{ "pcre_match_limit", Parameter::PT_INT, "0:max32", "1500",
"limit pcre backtracking, 0 = off" },
{ "pcre_match_limit_recursion", Parameter::PT_INT, "0:max32", "1500",
"limit pcre stack consumption, 0 = off" },
+ { "pcre_override", Parameter::PT_BOOL, nullptr, "true",
+ "enable pcre match limit overrides when pattern matching (ie ignore /O)" },
+
{ "enable_address_anomaly_checks", Parameter::PT_BOOL, nullptr, "false",
"enable check and alerting of address anomalies" },
else if ( v.is("global_rule_state") )
sc->global_rule_state = v.get_bool();
+#ifdef HAVE_HYPERSCAN
+ else if ( v.is("hyperscan_literals") )
+ sc->hyperscan_literals = v.get_bool();
+#endif
+
else if ( v.is("offload_limit") )
sc->offload_limit = v.get_uint32();
}
}
+ else if ( v.is("pcre_override") )
+ sc->pcre_override = v.get_bool();
+
else if ( v.is("enable_address_anomaly_checks") )
sc->address_anomaly_check_enabled = v.get_bool();
THREAD_LOCAL SnortConfig* snort_conf = nullptr;
uint32_t SnortConfig::warning_flags = 0;
-static std::vector <std::pair<ScScratchFunc,ScScratchFunc>> scratch_handlers;
+
+static std::vector<ScratchAllocator*> scratch_handlers;
//-------------------------------------------------------------------------
// private implementation
FreeClassifications(classifications);
FreeReferences(references);
- // Only call scratch cleanup if we actually called scratch setup
- if ( state and state[0].size() > 0 )
- {
- for ( unsigned i = scratch_handlers.size(); i > 0; i-- )
- {
- if ( scratch_handlers[i - 1].second )
- scratch_handlers[i - 1].second(this);
- }
- // FIXIT-L: Do we need to shrink_to_fit() state->scratch at this point?
- }
+ for ( auto* s : scratchers )
+ s->cleanup(this);
FreeRuleLists(this);
PortTablesFree(port_tables);
void SnortConfig::post_setup()
{
- unsigned i;
unsigned int handler_count = scratch_handlers.size();
// Ensure we have allocated the scratch space vector for each thread
- for ( i = 0; i < num_slots; ++i )
- {
+ for ( unsigned i = 0; i < num_slots; ++i )
state[i].resize(handler_count);
- }
- for ( i = 0; i < handler_count; ++i )
+ for ( auto* s : scratch_handlers )
{
- if ( scratch_handlers[i].first )
- scratch_handlers[i].first(this);
+ if ( s and s->setup(this) )
+ scratchers.push_back(s);
}
}
return (!((get_conf()->tunnel_mask & proto) or SFDAQ::get_tunnel_bypass(proto)));
}
-SO_PUBLIC int SnortConfig::request_scratch(ScScratchFunc setup, ScScratchFunc cleanup)
+SO_PUBLIC int SnortConfig::request_scratch(ScratchAllocator* s)
{
- scratch_handlers.emplace_back(std::make_pair(setup, cleanup));
+ scratch_handlers.emplace_back(s);
// We return an index that the caller uses to reference their per thread
// scratch space
return scratch_handlers.size() - 1;
}
+SO_PUBLIC void SnortConfig::release_scratch(int id)
+{
+ assert((unsigned)id < scratch_handlers.size());
+ scratch_handlers[id] = nullptr;
+}
+
SO_PUBLIC SnortConfig* SnortConfig::get_parser_conf()
{ return parser_conf; }
#include "events/event_queue.h"
#include "framework/bits.h"
+#include "helpers/scratch_allocator.h"
#include "main/policy.h"
#include "main/thread.h"
#include "sfip/sf_cidr.h"
struct XHash;
struct SnortConfig;
-typedef void (* ScScratchFunc)(SnortConfig* sc);
-
class ReloadResourceTuner
{
public:
// somehow a packet thread needs a much lower setting
long int pcre_match_limit = 1500;
long int pcre_match_limit_recursion = 1500;
+
int pcre_ovector_size = 0;
+ bool pcre_override = true;
int asn1_mem = 0;
uint32_t run_flags = 0;
unsigned offload_limit = 99999; // disabled
unsigned offload_threads = 0; // disabled
+#ifdef HAVE_HYPERSCAN
+ bool hyperscan_literals = false;
+#endif
+
bool global_rule_state = false;
bool global_default_rule_state = true;
MemoryConfig* memory = nullptr;
//------------------------------------------------------
+ std::vector<ScratchAllocator*> scratchers;
std::vector<void *>* state = nullptr;
unsigned num_slots = 0;
// This requests an entry in the scratch space vector and calls setup /
// cleanup as appropriate
- SO_PUBLIC static int request_scratch(ScScratchFunc setup, ScScratchFunc cleanup);
+ SO_PUBLIC static int request_scratch(ScratchAllocator*);
+ SO_PUBLIC static void release_scratch(int);
// Use this to access current thread's conf from other units
static void set_conf(SnortConfig*);
#include <cassert>
#include <cstring>
+#include "framework/module.h"
#include "framework/mpse.h"
+#include "helpers/scratch_allocator.h"
#include "log/messages.h"
#include "main/snort_config.h"
#include "main/thread.h"
using namespace snort;
+static const char* s_name = "hyperscan";
+static const char* s_help = "intel hyperscan-based mpse with regex support";
+
struct Pattern
{
std::string pat;
static std::vector<hs_scratch_t*> s_scratch;
static unsigned int scratch_index;
static bool scratch_registered = false;
+static ScratchAllocator* scratcher = nullptr;
+
+struct ScanContext
+{
+ class HyperscanMpse* mpse;
+ MpseMatch match_cb;
+ void* match_ctx;
+ int nfound = 0;
+
+ ScanContext(HyperscanMpse* m, MpseMatch cb, void* ctx)
+ { mpse = m; match_cb = cb; match_ctx = ctx; }
+
+};
//-------------------------------------------------------------------------
// mpse
int get_pattern_count() const override
{ return pvector.size(); }
- int match(unsigned id, unsigned long long to);
+ int match(unsigned id, unsigned long long to, MpseMatch match_cb, void* match_ctx);
static int match(
unsigned id, unsigned long long from, unsigned long long to,
hs_database_t* hs_db = nullptr;
- static THREAD_LOCAL MpseMatch match_cb;
- static THREAD_LOCAL void* match_ctx;
- static THREAD_LOCAL int nfound;
-
public:
static uint64_t instances;
static uint64_t patterns;
};
-THREAD_LOCAL MpseMatch HyperscanMpse::match_cb = nullptr;
-THREAD_LOCAL void* HyperscanMpse::match_ctx = nullptr;
-THREAD_LOCAL int HyperscanMpse::nfound = 0;
-
uint64_t HyperscanMpse::instances = 0;
uint64_t HyperscanMpse::patterns = 0;
return 0;
}
-int HyperscanMpse::match(unsigned id, unsigned long long to)
+int HyperscanMpse::match(unsigned id, unsigned long long to, MpseMatch match_cb, void* match_ctx)
{
assert(id < pvector.size());
Pattern& p = pvector[id];
- nfound++;
return match_cb(p.user, p.user_tree, (int)to, match_ctx, p.user_list);
}
unsigned id, unsigned long long /*from*/, unsigned long long to,
unsigned /*flags*/, void* pv)
{
- HyperscanMpse* h = (HyperscanMpse*)pv;
- return h->match(id, to);
+ ScanContext* scan = (ScanContext*)pv;
+ scan->nfound++;
+ return scan->mpse->match(id, to, scan->match_cb, scan->match_ctx);
}
int HyperscanMpse::_search(
const uint8_t* buf, int n, MpseMatch mf, void* pv, int* current_state)
{
*current_state = 0;
- nfound = 0;
-
- match_cb = mf;
- match_ctx = pv;
+ ScanContext scan(this, mf, pv);
hs_scratch_t* ss =
(hs_scratch_t*)SnortConfig::get_conf()->state[get_instance_id()][scratch_index];
// scratch is null for the degenerate case w/o patterns
assert(!hs_db or ss);
- hs_scan(hs_db, (const char*)buf, n, 0, ss,
- HyperscanMpse::match, this);
+ hs_scan(hs_db, (const char*)buf, n, 0, ss, HyperscanMpse::match, &scan);
- return nfound;
+ return scan.nfound;
}
-static void scratch_setup(SnortConfig* sc)
+static bool scratch_setup(SnortConfig* sc)
{
// find the largest scratch and clone for all slots
hs_scratch_t* max = nullptr;
+ if ( !s_scratch.size() )
+ return false;
+
for ( unsigned i = 0; i < sc->num_slots; ++i )
{
if ( !s_scratch[i] )
}
s_scratch[i] = nullptr;
}
+ if ( !max )
+ return false;
+
for ( unsigned i = 0; i < sc->num_slots; ++i )
{
hs_scratch_t** ss = (hs_scratch_t**) &sc->state[i][scratch_index];
-
- if ( max )
- hs_clone_scratch(max, ss);
- else
- *ss = nullptr;
+ hs_clone_scratch(max, ss);
}
- if ( max )
- hs_free_scratch(max);
+ hs_free_scratch(max);
+ return true;
}
static void scratch_cleanup(SnortConfig* sc)
{
for ( unsigned i = 0; i < sc->num_slots; ++i )
{
- hs_scratch_t* ss = (hs_scratch_t*) sc->state[i][scratch_index];
-
- if ( ss )
- {
- hs_free_scratch(ss);
- sc->state[i][scratch_index] = nullptr;
- }
+ hs_scratch_t* ss = (hs_scratch_t*)sc->state[i][scratch_index];
+ hs_free_scratch(ss);
+ sc->state[i][scratch_index] = nullptr;
}
}
+class HyperscanModule : public Module
+{
+public:
+ HyperscanModule() : Module(s_name, s_help)
+ {
+ scratcher = new SimpleScratchAllocator(scratch_setup, scratch_cleanup);
+ scratch_index = scratcher->get_id();
+ }
+
+ ~HyperscanModule() override
+ { delete scratcher; }
+};
+
//-------------------------------------------------------------------------
// api
//-------------------------------------------------------------------------
+static Module* mod_ctor()
+{ return new HyperscanModule; }
+
+static void mod_dtor(Module* p)
+{ delete p; }
+
static Mpse* hs_ctor(
SnortConfig* sc, class Module*, const MpseAgent* a)
{
if ( !scratch_registered )
{
s_scratch.resize(sc->num_slots);
- scratch_index = SnortConfig::request_scratch(scratch_setup, scratch_cleanup);
+ scratch_index = scratcher->get_id();
scratch_registered = true;
}
return new HyperscanMpse(sc, a);
0,
API_RESERVED,
API_OPTIONS,
- "hyperscan",
- "intel hyperscan-based mpse with regex support",
- nullptr,
- nullptr
+ s_name,
+ s_help,
+ mod_ctor,
+ mod_dtor
},
MPSE_REGEX | MPSE_MTBLD,
nullptr, // activate
num = mpsegrp->get_offload_mpse()->search((const uint8_t*)str, len, mf, user_data, &state);
if ( num < 0 )
- num = mpsegrp->get_normal_mpse()->search((const uint8_t*)str, len, mf, user_data,
- &state);
+ num = mpsegrp->get_normal_mpse()->search((const uint8_t*)str, len, mf, user_data, &state);
}
else
num = mpsegrp->get_normal_mpse()->search((const uint8_t*)str, len, mf, user_data, &state);
if ( HAVE_HYPERSCAN )
add_cpputest( hyperscan_test
- SOURCES ../hyperscan.cc
+ SOURCES
+ ../hyperscan.cc
+ ../../framework/module.cc
+ ../../helpers/scratch_allocator.cc
+ ../../helpers/hyper_scratch_allocator.cc
LIBS ${HS_LIBRARIES}
)
endif()
#include <string.h>
#include "framework/base_api.h"
+#include "framework/counts.h"
#include "framework/mpse.h"
#include "framework/mpse_batch.h"
#include "main/snort_config.h"
+#include "utils/stats.h"
// must appear after snort_config.h to avoid broken c++ map include
#include <CppUTest/CommandLineTestRunner.h>
//-------------------------------------------------------------------------
// base stuff
//-------------------------------------------------------------------------
+
namespace snort
{
Mpse::Mpse(const char*) { }
THREAD_LOCAL SnortConfig* snort_conf = &s_conf;
static std::vector<void *> s_state;
-
-ScScratchFunc scratch_setup;
-ScScratchFunc scratch_cleanup;
+static ScratchAllocator* scratcher = nullptr;
SnortConfig::SnortConfig(const SnortConfig* const)
{
SnortConfig::~SnortConfig() = default;
-int SnortConfig::request_scratch(ScScratchFunc setup, ScScratchFunc cleanup)
+int SnortConfig::request_scratch(ScratchAllocator* s)
{
- scratch_setup = setup;
- scratch_cleanup = cleanup;
+ scratcher = s;
s_state.resize(1);
-
return 0;
}
+void SnortConfig::release_scratch(int) { }
+
SnortConfig* SnortConfig::get_conf()
{ return snort_conf; }
{ return 0; }
}
+void show_stats(PegCount*, const PegInfo*, unsigned, const char*) { }
+void show_stats(PegCount*, const PegInfo*, const IndexVec&, const char*, FILE*) { }
+
//-------------------------------------------------------------------------
// stubs, spies, etc.
//-------------------------------------------------------------------------
TEST_GROUP(mpse_hs_match)
{
+ Module* mod = nullptr;
Mpse* hs = nullptr;
+ bool do_cleanup = false;
const MpseApi* mpse_api = (MpseApi*)se_hyperscan;
void setup() override
// FIXIT-L cpputest hangs or crashes in the leak detector
MemoryLeakWarningPlugin::turnOffNewDeleteOverloads();
CHECK(se_hyperscan);
+ mod = mpse_api->base.mod_ctor();
hs = mpse_api->ctor(snort_conf, nullptr, &s_agent);
CHECK(hs);
hits = 0;
void teardown() override
{
mpse_api->dtor(hs);
- scratch_cleanup(snort_conf);
+ if ( do_cleanup )
+ scratcher->cleanup(snort_conf);
+ mpse_api->base.mod_dtor(mod);
MemoryLeakWarningPlugin::turnOnNewDeleteOverloads();
}
};
CHECK(parse_errors == 0);
CHECK(hs->get_pattern_count() == 0);
+ do_cleanup = scratcher->setup(snort_conf);
+
int state = 0;
CHECK(hs->search((uint8_t*)"foo", 3, match, nullptr, &state) == 0);
CHECK(hits == 0);
CHECK(hs->prep_patterns(snort_conf) == 0);
CHECK(hs->get_pattern_count() == 1);
- scratch_setup(snort_conf);
+ do_cleanup = scratcher->setup(snort_conf);
int state = 0;
CHECK(hs->search((uint8_t*)"foo", 3, match, nullptr, &state) == 1);
CHECK(hs->prep_patterns(snort_conf) == 0);
CHECK(hs->get_pattern_count() == 1);
- scratch_setup(snort_conf);
+ do_cleanup = scratcher->setup(snort_conf);
int state = 0;
CHECK(hs->search((uint8_t*)"foo", 3, match, nullptr, &state) == 1);
CHECK(hs->prep_patterns(snort_conf) == 0);
CHECK(hs->get_pattern_count() == 1);
- scratch_setup(snort_conf);
+ do_cleanup = scratcher->setup(snort_conf);
int state = 0;
CHECK(hs->search((uint8_t*)"foo", 3, match, nullptr, &state) == 1);
CHECK(hs->prep_patterns(snort_conf) == 0);
CHECK(hs->get_pattern_count() == 3);
- scratch_setup(snort_conf);
+
+ do_cleanup = scratcher->setup(snort_conf);
int state = 0;
CHECK(hs->search((uint8_t*)"foo bar baz", 11, match, nullptr, &state) == 3);
CHECK(hs->prep_patterns(snort_conf) == 0);
CHECK(hs->get_pattern_count() == 1);
- scratch_setup(snort_conf);
+
+ do_cleanup = scratcher->setup(snort_conf);
int state = 0;
CHECK(hs->search((uint8_t*)"foo bar baz", 11, match, nullptr, &state) == 0);
CHECK(hs->prep_patterns(snort_conf) == 0);
CHECK(hs->get_pattern_count() == 1);
- scratch_setup(snort_conf);
+
+ do_cleanup = scratcher->setup(snort_conf);
int state = 0;
CHECK(hs->search((uint8_t*)":definition(", 12, match, nullptr, &state) == 0);
TEST_GROUP(mpse_hs_multi)
{
+ Module* mod = nullptr;
Mpse* hs1 = nullptr;
Mpse* hs2 = nullptr;
+ bool do_cleanup = false;
const MpseApi* mpse_api = (MpseApi*)se_hyperscan;
void setup() override
MemoryLeakWarningPlugin::turnOffNewDeleteOverloads();
CHECK(se_hyperscan);
+ mod = mpse_api->base.mod_ctor();
+
hs1 = mpse_api->ctor(snort_conf, nullptr, &s_agent);
CHECK(hs1);
{
mpse_api->dtor(hs1);
mpse_api->dtor(hs2);
- scratch_cleanup(snort_conf);
+ if ( do_cleanup )
+ scratcher->cleanup(snort_conf);
+ mpse_api->base.mod_dtor(mod);
MemoryLeakWarningPlugin::turnOnNewDeleteOverloads();
}
};
CHECK(hs1->get_pattern_count() == 1);
CHECK(hs2->get_pattern_count() == 1);
- scratch_setup(snort_conf);
+ do_cleanup = scratcher->setup(snort_conf);
int state = 0;
CHECK(hs1->search((uint8_t*)"fubar", 5, match, nullptr, &state) == 1 );
add_library ( utils OBJECT
${UTIL_INCLUDES}
${SNPRINTF_SOURCES}
- boyer_moore.cc
+ boyer_moore.cc
dnet_header.h
dyn_array.cc
dyn_array.h
kmap.cc
- segment_mem.cc
- sflsq.cc
- sfmemcap.cc
+ segment_mem.cc
+ sflsq.cc
+ sfmemcap.cc
snort_bounds.h
stats.cc
util.cc
util_ber.cc
util_cstring.cc
- util_jsnorm.cc
- util_net.cc
+ util_jsnorm.cc
+ util_net.cc
util_net.h
- util_unfold.cc
- util_utf.cc
+ util_unfold.cc
+ util_utf.cc
${TEST_FILES}
)