#include "config.h"
#endif
-#include <pcre.h>
-
#include <cassert>
#include "detection/ips_context.h"
#include "framework/parameter.h"
#include "framework/pig_pen.h"
#include "hash/hash_key_operations.h"
-#include "helpers/scratch_allocator.h"
#include "log/log_stats.h"
#include "log/messages.h"
#include "main/snort_config.h"
#include "managers/ips_manager.h"
#include "managers/module_manager.h"
#include "profiler/profiler.h"
+#include "utils/snort_pcre.h"
#include "utils/stats.h"
#include "utils/util.h"
-using namespace snort;
-
-#ifndef PCRE_STUDY_JIT_COMPILE
-#define PCRE_STUDY_JIT_COMPILE 0
-#endif
-
-//#define NO_JIT // uncomment to disable JIT for Xcode
+// Unset to disable JIT for Xcode
+#define USE_JIT 1
-#ifdef NO_JIT
-#define PCRE_STUDY_FLAGS 0
-#define pcre_release(x) pcre_free(x)
-#else
-#define PCRE_STUDY_FLAGS PCRE_STUDY_JIT_COMPILE
-#define pcre_release(x) pcre_free_study(x)
-#endif
+using namespace snort;
#define SNORT_PCRE_RELATIVE 0x00010 // relative to the end of the last match
#define SNORT_PCRE_INVERT 0x00020 // invert detect
struct PcreData
{
- pcre* re; /* compiled regex */
- pcre_extra* pe; /* studied regex foo */
- bool free_pe;
- int options; /* sp_pcre specific options (relative & inverse) */
+ pcre2_code* re; /* compiled regex */
+ pcre2_match_context* match_context; /* match_context */
+ pcre2_match_data* match_data; /* match data space for storing results */
+ int options; /* sp_pcre specific options (relative & inverse) */
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.
-
-// 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_max = -1;
-
-static unsigned scratch_index;
-static ScratchAllocator* scratcher = nullptr;
-
static THREAD_LOCAL ProfileStats pcrePerfStats;
struct PcreCounts
// implementation foo
//-------------------------------------------------------------------------
-static void pcre_capture(
- const void* code, const void* extra)
-{
- int tmp_ovector_size = 0;
-
- pcre_fullinfo((const pcre*)code, (const pcre_extra*)extra,
- PCRE_INFO_CAPTURECOUNT, &tmp_ovector_size);
-
- if (tmp_ovector_size > s_ovector_max)
- s_ovector_max = tmp_ovector_size;
-}
-
static void pcre_check_anchored(PcreData* pcre_data)
{
int rc;
unsigned long int options = 0;
- if ((pcre_data == nullptr) || (pcre_data->re == nullptr) || (pcre_data->pe == nullptr))
+ if ((pcre_data == nullptr) || (pcre_data->re == nullptr))
return;
- rc = pcre_fullinfo(pcre_data->re, pcre_data->pe, PCRE_INFO_OPTIONS, (void*)&options);
+ rc = pcre2_pattern_info(pcre_data->re, PCRE2_INFO_ARGOPTIONS, (void*)&options);
switch (rc)
{
/* pcre_fullinfo fails for the following:
/* This is the success code */
break;
- case PCRE_ERROR_NULL:
- ParseError("pcre_fullinfo: code and/or where were null.");
+ case PCRE2_ERROR_NULL:
+ ParseError("pcre2: code and/or where were null.");
return;
- case PCRE_ERROR_BADMAGIC:
- ParseError("pcre_fullinfo: compiled code didn't have correct magic.");
+ case PCRE2_ERROR_BADMAGIC:
+ ParseError("pcre2: compiled code didn't have correct magic.");
return;
- case PCRE_ERROR_BADOPTION:
- ParseError("pcre_fullinfo: option type is invalid.");
+ case PCRE2_ERROR_BADOPTION:
+ ParseError("pcre2: option type is invalid.");
return;
default:
- ParseError("pcre_fullinfo: Unknown error code.");
+ ParseError("pcre2: Unknown error code.");
return;
}
- if ((options & PCRE_ANCHORED) && !(options & PCRE_MULTILINE))
+ if ((options & PCRE2_ANCHORED) && !(options & PCRE2_MULTILINE))
{
/* This means that this pcre rule option shouldn't be EvalStatus
* even if any of it's relative children should fail to match.
static void pcre_parse(const SnortConfig* sc, const char* data, PcreData* pcre_data)
{
- const char* error;
+ PCRE2_UCHAR error[128];
char* re, * free_me;
char* opts;
char delimit = '/';
- int erroffset;
+ int errorcode;
+ PCRE2_SIZE erroffset;
int compile_flags = 0;
if (data == nullptr)
{
switch (*opts)
{
- case 'i': compile_flags |= PCRE_CASELESS; break;
- case 's': compile_flags |= PCRE_DOTALL; break;
- case 'm': compile_flags |= PCRE_MULTILINE; break;
- case 'x': compile_flags |= PCRE_EXTENDED; break;
+ case 'i': compile_flags |= PCRE2_CASELESS; break;
+ case 's': compile_flags |= PCRE2_DOTALL; break;
+ case 'm': compile_flags |= PCRE2_MULTILINE; break;
+ case 'x': compile_flags |= PCRE2_EXTENDED; break;
/*
* these are pcre specific... don't work with perl
*/
- case 'A': compile_flags |= PCRE_ANCHORED; break;
- case 'E': compile_flags |= PCRE_DOLLAR_ENDONLY; break;
- case 'G': compile_flags |= PCRE_UNGREEDY; break;
+ case 'A': compile_flags |= PCRE2_ANCHORED; break;
+ case 'E': compile_flags |= PCRE2_DOLLAR_ENDONLY; break;
+ case 'G': compile_flags |= PCRE2_UNGREEDY; break;
/*
* these are snort specific don't work with pcre or perl
}
/* now compile the re */
- pcre_data->re = pcre_compile(re, compile_flags, &error, &erroffset, nullptr);
+ pcre_data->re = pcre2_compile((PCRE2_SPTR)re, PCRE2_ZERO_TERMINATED, compile_flags,
+ &errorcode, &erroffset, nullptr);
if (pcre_data->re == nullptr)
{
- ParseError(": pcre compile of '%s' failed at offset "
- "%d : %s", re, erroffset, error);
+ pcre2_get_error_message(errorcode, error, sizeof(error)/sizeof(char));
+ ParseError("pcre2 compile of '%s' failed at offset "
+ "%zu : %s", re, erroffset, error);
return;
}
- /* now study it... */
- pcre_data->pe = pcre_study(pcre_data->re, PCRE_STUDY_FLAGS, &error);
-
- if (pcre_data->pe)
+ /* create match context */
+ pcre_data->match_context = pcre2_match_context_create(NULL);
+ if (pcre_data->match_context == NULL)
{
- if ((sc->get_pcre_match_limit() != 0) &&
- !(pcre_data->options & SNORT_OVERRIDE_MATCH_LIMIT))
- {
- if ( !(pcre_data->pe->flags & PCRE_EXTRA_MATCH_LIMIT) )
- pcre_data->pe->flags |= PCRE_EXTRA_MATCH_LIMIT;
-
- pcre_data->pe->match_limit = sc->get_pcre_match_limit();
- }
+ ParseError("failed to allocate memory for match context");
+ return;
+ }
- if ((sc->get_pcre_match_limit_recursion() != 0) &&
- !(pcre_data->options & SNORT_OVERRIDE_MATCH_LIMIT))
- {
- if ( !(pcre_data->pe->flags & PCRE_EXTRA_MATCH_LIMIT_RECURSION) )
- pcre_data->pe->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION;
+ pcre_data->match_data = pcre2_match_data_create_from_pattern(pcre_data->re, nullptr);
- pcre_data->pe->match_limit_recursion =
- sc->get_pcre_match_limit_recursion();
- }
- }
- else
+ /* now study it... */
+ if (USE_JIT)
{
- if (!(pcre_data->options & SNORT_OVERRIDE_MATCH_LIMIT) &&
- ((sc->get_pcre_match_limit() != 0) ||
- (sc->get_pcre_match_limit_recursion() != 0)))
+ // It is possible that we fail to study a re with JIT. In that case,
+ // we fallback to normal processing (as non-JIT)
+ errorcode = pcre2_jit_compile(pcre_data->re, PCRE2_JIT_COMPLETE);
+
+ if (errorcode)
{
- pcre_data->pe = (pcre_extra*)snort_calloc(sizeof(pcre_extra));
- pcre_data->free_pe = true;
-
- if (sc->get_pcre_match_limit() != 0)
- {
- pcre_data->pe->flags |= PCRE_EXTRA_MATCH_LIMIT;
- pcre_data->pe->match_limit = sc->get_pcre_match_limit();
- }
-
- if (sc->get_pcre_match_limit_recursion() != 0)
- {
- pcre_data->pe->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION;
- pcre_data->pe->match_limit_recursion =
- sc->get_pcre_match_limit_recursion();
- }
+ pcre2_get_error_message(errorcode, error, sizeof(error)/sizeof(char));
+ ParseWarning(WARN_RULES, "pcre2 JIT compile of '%s' failed : %s\n", re, error);
}
}
- if (error != nullptr)
- {
- ParseError("pcre study failed : %s", error);
- return;
- }
+ if ((sc->get_pcre_match_limit() != 0) && !(pcre_data->options & SNORT_OVERRIDE_MATCH_LIMIT))
+ pcre2_set_match_limit(pcre_data->match_context, sc->get_pcre_match_limit());
- pcre_capture(pcre_data->re, pcre_data->pe);
- pcre_check_anchored(pcre_data);
+ if ((sc->get_pcre_match_limit_recursion() != 0) && !(pcre_data->options & SNORT_OVERRIDE_MATCH_LIMIT))
+ pcre2_set_depth_limit(pcre_data->match_context, sc->get_pcre_match_limit_recursion());
+ pcre_check_anchored(pcre_data);
snort_free(free_me);
return;
* found_offset will be set to -1 when the find is unsuccessful OR the routine is inverted
*/
static bool pcre_search(
- Packet* p,
+ Packet*,
const PcreData* pcre_data,
const uint8_t* buf,
unsigned len,
unsigned start_offset,
int& found_offset)
{
+ PCRE2_SIZE* ovector;
bool matched;
found_offset = -1;
- std::vector<void *> ss = p->context->conf->state[get_instance_id()];
- assert(ss[scratch_index]);
-
- int result = pcre_exec(
- pcre_data->re, /* result of pcre_compile() */
- pcre_data->pe, /* result of pcre_study() */
- (const char*)buf, /* the subject string */
- len, /* the length of the subject string */
- start_offset, /* start at offset 0 in the subject */
- 0, /* options(handled at compile time */
- (int*)ss[scratch_index], /* vector for substring information */
- p->context->conf->pcre_ovector_size); /* number of elements in the vector */
+ int result = pcre2_match(
+ pcre_data->re, /* result of pcre_compile() */
+ (PCRE2_SPTR)buf, /* the subject string */
+ (PCRE2_SIZE)len, /* the length of the subject string */
+ (PCRE2_SIZE)start_offset, /* start at offset 0 in the subject */
+ 0, /* options (handled at compile time) */
+ pcre_data->match_data, /* match data to store the match results */
+ pcre_data->match_context); /* match context for limits */
if (result >= 0)
{
* ovector[1], identify the portion of the subject string matched
* by the entire pattern. The next pair is used for the first
* capturing subpattern, and so on. The value returned by
- * pcre_exec() is the number of pairs that have been set. If there
+ * pcre_search() is the number of pairs that have been set. If there
* are no capturing subpatterns, the return value from a successful
* match is 1, indicating that just the first pair of offsets has
* been set.
* and a single int for scratch space.
*/
- found_offset = ((int*)ss[scratch_index])[1];
+ ovector = pcre2_get_ovector_pointer(pcre_data->match_data);
+ found_offset = ovector[1];
}
- else if (result == PCRE_ERROR_NOMATCH)
+ else if (result == PCRE2_ERROR_NOMATCH)
{
matched = false;
}
- else if (result == PCRE_ERROR_MATCHLIMIT)
+ else if (result == PCRE2_ERROR_MATCHLIMIT)
{
pcre_stats.pcre_match_limit++;
matched = false;
}
- else if (result == PCRE_ERROR_RECURSIONLIMIT)
+ else if (result == PCRE2_ERROR_RECURSIONLIMIT)
{
pcre_stats.pcre_recursion_limit++;
matched = false;
if ( config->expression )
snort_free(config->expression);
- if ( config->pe )
- {
- if ( config->free_pe )
- snort_free(config->pe);
- else
- pcre_release(config->pe);
- }
-
- if ( config->re )
- free(config->re); // external allocation
+ pcre2_match_context_free(config->match_context);
+ pcre2_code_free(config->re);
+ pcre2_match_data_free(config->match_data);
snort_free(config);
}
{
public:
PcreModule() : Module(s_name, s_help, s_params)
- {
- data = nullptr;
- scratcher = new SimpleScratchAllocator(scratch_setup, scratch_cleanup);
- scratch_index = scratcher->get_id();
- }
+ { data = nullptr; }
~PcreModule() override
- {
- delete data;
- delete scratcher;
- }
+ { delete data; }
#ifdef HAVE_HYPERSCAN
bool begin(const char*, int, SnortConfig*) override;
PcreData* data;
Module* mod_regex = nullptr;
std::string re;
-
- static bool scratch_setup(SnortConfig*);
- static void scratch_cleanup(SnortConfig*);
};
PcreData* PcreModule::get_data()
if( mod_regex )
mod_regex = mod_regex->begin(name, v, sc) ? mod_regex : nullptr;
}
+
return true;
}
#endif
return true;
}
-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(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];
- snort_free(ss[scratch_index]);
- ss[scratch_index] = nullptr;
- }
-}
-
//-------------------------------------------------------------------------
// api methods
//-------------------------------------------------------------------------
#include "lua_detector_api.h"
#include <lua.hpp>
-#include <pcre.h>
#include <unordered_map>
#include "detection/fp_config.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "trace/trace_api.h"
+#include "utils/snort_pcre.h"
#include "app_info_table.h"
#include "appid_debug.h"
// Verify detector user data and that we are in packet context
LuaStateDescriptor* lsd = ud->validate_lua_state(true);
- int ovector[OVECCOUNT];
- const char* error;
- int erroffset;
+ pcre2_match_data* match_data;
+ PCRE2_UCHAR error[128];
+ PCRE2_SIZE erroffset;
+ int errorcode;
const char* pattern = lua_tostring(L, 2);
unsigned int offset = lua_tonumber(L, 3); /*offset can be zero, no check necessary. */
/*compile the regular expression pattern, and handle errors */
- pcre* re = pcre_compile(pattern, // the pattern
- PCRE_DOTALL, // default options - dot matches all inc \n
- &error, // for error message
- &erroffset, // for error offset
- nullptr); // use default character tables
+ pcre2_code* re = pcre2_compile((PCRE2_SPTR)pattern, // the pattern
+ PCRE2_ZERO_TERMINATED, // assume zero terminated strings
+ PCRE2_DOTALL, // default options - dot matches all inc \n
+ &errorcode, // for error message
+ &erroffset, // for error offset
+ nullptr); // default character tables
if (re == nullptr)
{
+ pcre2_get_error_message(errorcode, error, 128);
appid_log(lsd->ldp.pkt, TRACE_ERROR_LEVEL, "PCRE compilation failed at offset %d: %s\n", erroffset, error);
return 0;
}
- /*pattern match against the subject string. */
- int rc = pcre_exec(re, // compiled pattern
- nullptr, // no extra data
- (const char*)lsd->ldp.data, // subject string
- lsd->ldp.size, // length of the subject
- offset, // offset 0
- 0, // default options
- ovector, // output vector for substring information
- OVECCOUNT); // number of elements in the output vector
+ match_data = pcre2_match_data_create(OVECCOUNT, NULL);
+ if (!match_data)
+ {
+ appid_log(lsd->ldp.pkt, TRACE_ERROR_LEVEL, "PCRE failed to allocate mem for match_data\n");
+ return 0;
+ }
+
+ int rc = pcre2_match(re, // compiled pattern
+ (PCRE2_SPTR)lsd->ldp.data, // subject string
+ (PCRE2_SIZE)lsd->ldp.size, // length of the subject
+ (PCRE2_SIZE)offset, // offset 0
+ 0, // default options
+ match_data, // match data for match results
+ NULL); // no match context
if (rc >= 0)
{
return 0;
}
+ PCRE2_SIZE* ovector = pcre2_get_ovector_pointer(match_data);
for (int i = 0; i < rc; i++)
{
lua_pushlstring(L, (const char*)lsd->ldp.data + ovector[2*i], ovector[2*i+1] -
else
{
// log errors except no matches
- if (rc != PCRE_ERROR_NOMATCH)
+ if (rc != PCRE2_ERROR_NOMATCH)
appid_log(lsd->ldp.pkt, TRACE_WARNING_LEVEL, "PCRE regular expression group match failed. rc: %d\n", rc);
rc = 0;
}
- pcre_free(re);
+ pcre2_match_data_free(match_data);
+ pcre2_code_free(re);
return rc;
}