From: Jeff Lucovsky Date: Mon, 18 Nov 2019 22:02:17 +0000 (-0500) Subject: detect/pcre: Changes to support pcre_jit_exec X-Git-Tag: suricata-6.0.0-beta1~622 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=abe0cdc4adc872f346a94c554883570783917034;p=thirdparty%2Fsuricata.git detect/pcre: Changes to support pcre_jit_exec This command causes `pcre_jit_exec` to be used when available. If it's available and there are allocation errors preparing for it, things fallback to `pcre_exec`. --- diff --git a/src/detect-pcre.c b/src/detect-pcre.c index 7ca3c9c5a7..bc0bca6f43 100644 --- a/src/detect-pcre.c +++ b/src/detect-pcre.c @@ -24,6 +24,7 @@ */ #include "suricata-common.h" +#include "pcre.h" #include "debug.h" #include "decode.h" #include "detect.h" @@ -70,15 +71,41 @@ static int pcre_match_limit = 0; static int pcre_match_limit_recursion = 0; -static pcre *parse_regex; -static pcre_extra *parse_regex_study; -static pcre *parse_capture_regex; -static pcre_extra *parse_capture_regex_study; +static DetectParseRegex parse_regex; +static DetectParseRegex parse_capture_regex; #ifdef PCRE_HAVE_JIT static int pcre_use_jit = 1; #endif +#ifdef PCRE_HAVE_JIT_EXEC +#define PCRE_JIT_MIN_STACK 32*1024 +#define PCRE_JIT_MAX_STACK 512*1024 + +static int g_jitstack_thread_ctx_id = -1; + +#endif + +/* \brief Helper function for using pcre_exec with/without JIT + */ +static int DetectPcreExec(DetectEngineThreadCtx *det_ctx, DetectParseRegex *regex, const char *str, const size_t strlen, + int start_offset, int options, int *ovector, int ovector_size) +{ +#ifdef PCRE_HAVE_JIT_EXEC + if (det_ctx && g_jitstack_thread_ctx_id != -1) { + pcre_jit_stack *jit_stack = (pcre_jit_stack *) + DetectThreadCtxGetKeywordThreadCtx(det_ctx, g_jitstack_thread_ctx_id); + if (jit_stack) { + return pcre_jit_exec(regex->regex, regex->study, str, strlen, + start_offset, options, ovector, ovector_size, + jit_stack); + } + } +#endif + return pcre_exec(regex->regex, regex->study, str, strlen, + start_offset, options, ovector, ovector_size); +} + static int DetectPcreSetup (DetectEngineCtx *, Signature *, const char *); static void DetectPcreFree(void *); static void DetectPcreRegisterTests(void); @@ -124,24 +151,11 @@ void DetectPcreRegister (void) } } - DetectSetupParseRegexes(PARSE_REGEX, &parse_regex, &parse_regex_study); + DetectSetupParseRegexes(PARSE_REGEX, &parse_regex); /* setup the capture regex, as it needs PCRE_UNGREEDY we do it manually */ - const char *eb; - int eo; int opts = PCRE_UNGREEDY; /* pkt_http_ua should be pkt, http_ua, for this reason the UNGREEDY */ - - parse_capture_regex = pcre_compile(PARSE_CAPTURE_REGEX, opts, &eb, &eo, NULL); - if (parse_capture_regex == NULL) - { - FatalError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_CAPTURE_REGEX, eo, eb); - } - - parse_capture_regex_study = pcre_study(parse_capture_regex, 0, &eb); - if(eb != NULL) - { - FatalError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb); - } + DetectSetupParseRegexesOpts(PARSE_CAPTURE_REGEX, &parse_capture_regex, opts); #ifdef PCRE_HAVE_JIT if (PageSupportsRWX() == 0) { @@ -150,7 +164,6 @@ void DetectPcreRegister (void) } #endif - DetectParseRegexAddToFreeList(parse_capture_regex, parse_capture_regex_study); return; } @@ -173,7 +186,6 @@ int DetectPcrePayloadMatch(DetectEngineThreadCtx *det_ctx, const Signature *s, const uint8_t *payload, uint32_t payload_len) { SCEnter(); -#define MAX_SUBSTRINGS 30 int ret = 0; int ov[MAX_SUBSTRINGS]; const uint8_t *ptr = NULL; @@ -196,7 +208,7 @@ int DetectPcrePayloadMatch(DetectEngineThreadCtx *det_ctx, const Signature *s, } /* run the actual pcre detection */ - ret = pcre_exec(pe->re, pe->sd, (char *)ptr, len, start_offset, 0, ov, MAX_SUBSTRINGS); + ret = DetectPcreExec(det_ctx, &pe->parse_regex, (char *) ptr, len, start_offset, 0, ov, MAX_SUBSTRINGS); SCLogDebug("ret %d (negating %s)", ret, (pe->flags & DETECT_PCRE_NEGATE) ? "set" : "not set"); if (ret == PCRE_ERROR_NOMATCH) { @@ -334,7 +346,6 @@ static DetectPcreData *DetectPcreParse (DetectEngineCtx *de_ctx, int opts = 0; DetectPcreData *pd = NULL; char *op = NULL; -#define MAX_SUBSTRINGS 30 int ret = 0, res = 0; int ov[MAX_SUBSTRINGS]; int check_host_header = 0; @@ -381,8 +392,8 @@ static DetectPcreData *DetectPcreParse (DetectEngineCtx *de_ctx, } char re[slen]; - ret = pcre_exec(parse_regex, parse_regex_study, regexstr, slen, - 0, 0, ov, MAX_SUBSTRINGS); + ret = DetectPcreExec(NULL, &parse_regex, regexstr, slen, + 0, 0, ov, MAX_SUBSTRINGS); if (ret <= 0) { SCLogError(SC_ERR_PCRE_MATCH, "pcre parse error: %s", regexstr); goto error; @@ -406,10 +417,9 @@ static DetectPcreData *DetectPcreParse (DetectEngineCtx *de_ctx, } //printf("ret %" PRId32 " re \'%s\', op \'%s\'\n", ret, re, op); - pd = SCMalloc(sizeof(DetectPcreData)); + pd = SCCalloc(1, sizeof(DetectPcreData)); if (unlikely(pd == NULL)) goto error; - memset(pd, 0, sizeof(DetectPcreData)); if (negate) pd->flags |= DETECT_PCRE_NEGATE; @@ -613,13 +623,13 @@ static DetectPcreData *DetectPcreParse (DetectEngineCtx *de_ctx, if (capture_names == NULL || strlen(capture_names) == 0) opts |= PCRE_NO_AUTO_CAPTURE; - pd->re = pcre_compile2(re, opts, &ec, &eb, &eo, NULL); - if (pd->re == NULL && ec == 15) { // reference to non-existent subpattern + pd->parse_regex.regex = pcre_compile2(re, opts, &ec, &eb, &eo, NULL); + if (pd->parse_regex.regex == NULL && ec == 15) { // reference to non-existent subpattern opts &= ~PCRE_NO_AUTO_CAPTURE; - pd->re = pcre_compile(re, opts, &eb, &eo, NULL); + pd->parse_regex.regex = pcre_compile(re, opts, &eb, &eo, NULL); } - if (pd->re == NULL) { + if (pd->parse_regex.regex == NULL) { SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed " "at offset %" PRId32 ": %s", regexstr, eo, eb); goto error; @@ -630,7 +640,7 @@ static DetectPcreData *DetectPcreParse (DetectEngineCtx *de_ctx, if (pcre_use_jit) options |= PCRE_STUDY_JIT_COMPILE; #endif - pd->sd = pcre_study(pd->re, options, &eb); + pd->parse_regex.study = pcre_study(pd->parse_regex.regex, options, &eb); if(eb != NULL) { SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed : %s", eb); goto error; @@ -638,7 +648,7 @@ static DetectPcreData *DetectPcreParse (DetectEngineCtx *de_ctx, #ifdef PCRE_HAVE_JIT int jit = 0; - ret = pcre_fullinfo(pd->re, pd->sd, PCRE_INFO_JIT, &jit); + ret = pcre_fullinfo(pd->parse_regex.regex, pd->parse_regex.study, PCRE_INFO_JIT, &jit); if (ret != 0 || jit != 1) { /* warning, so we won't print the sig after this. Adding * file and line to the message so the admin can figure @@ -647,29 +657,30 @@ static DetectPcreData *DetectPcreParse (DetectEngineCtx *de_ctx, "Falling back to regular PCRE handling (%s:%d)", regexstr, de_ctx->rule_file, de_ctx->rule_line); } + #endif /*PCRE_HAVE_JIT*/ - if (pd->sd == NULL) - pd->sd = (pcre_extra *) SCCalloc(1,sizeof(pcre_extra)); + if (pd->parse_regex.study == NULL) + pd->parse_regex.study = (pcre_extra *) SCCalloc(1,sizeof(pcre_extra)); - if (pd->sd) { + if (pd->parse_regex.study) { if(pd->flags & DETECT_PCRE_MATCH_LIMIT) { if(pcre_match_limit >= -1) { - pd->sd->match_limit = pcre_match_limit; - pd->sd->flags |= PCRE_EXTRA_MATCH_LIMIT; + pd->parse_regex.study->match_limit = pcre_match_limit; + pd->parse_regex.study->flags |= PCRE_EXTRA_MATCH_LIMIT; } #ifndef NO_PCRE_MATCH_RLIMIT if(pcre_match_limit_recursion >= -1) { - pd->sd->match_limit_recursion = pcre_match_limit_recursion; - pd->sd->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION; + pd->parse_regex.study->match_limit_recursion = pcre_match_limit_recursion; + pd->parse_regex.study->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION; } #endif /* NO_PCRE_MATCH_RLIMIT */ } else { - pd->sd->match_limit = SC_MATCH_LIMIT_DEFAULT; - pd->sd->flags |= PCRE_EXTRA_MATCH_LIMIT; + pd->parse_regex.study->match_limit = SC_MATCH_LIMIT_DEFAULT; + pd->parse_regex.study->flags |= PCRE_EXTRA_MATCH_LIMIT; #ifndef NO_PCRE_MATCH_RLIMIT - pd->sd->match_limit_recursion = SC_MATCH_LIMIT_RECURSION_DEFAULT; - pd->sd->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION; + pd->parse_regex.study->match_limit_recursion = SC_MATCH_LIMIT_RECURSION_DEFAULT; + pd->parse_regex.study->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION; #endif /* NO_PCRE_MATCH_RLIMIT */ } } else { @@ -678,12 +689,7 @@ static DetectPcreData *DetectPcreParse (DetectEngineCtx *de_ctx, return pd; error: - if (pd != NULL && pd->re != NULL) - pcre_free(pd->re); - if (pd != NULL && pd->sd != NULL) - pcre_free_study(pd->sd); - if (pd) - SCFree(pd); + DetectPcreFree(pd); return NULL; } @@ -705,7 +711,7 @@ static int DetectPcreParseCapture(const char *regexstr, DetectEngineCtx *de_ctx, SCLogDebug("regexstr %s, pd %p", regexstr, pd); - ret = pcre_fullinfo(pd->re, pd->sd, PCRE_INFO_CAPTURECOUNT, &capture_cnt); + ret = pcre_fullinfo(pd->parse_regex.regex, pd->parse_regex.study, PCRE_INFO_CAPTURECOUNT, &capture_cnt); SCLogDebug("ret %d capture_cnt %d", ret, capture_cnt); if (ret == 0 && capture_cnt && strlen(capture_names) > 0) { @@ -771,7 +777,7 @@ static int DetectPcreParseCapture(const char *regexstr, DetectEngineCtx *de_ctx, while (1) { SCLogDebug("\'%s\'", regexstr); - ret = pcre_exec(parse_capture_regex, parse_capture_regex_study, regexstr, strlen(regexstr), 0, 0, ov, MAX_SUBSTRINGS); + ret = DetectPcreExec(NULL, &parse_capture_regex, regexstr, strlen(regexstr), 0, 0, ov, MAX_SUBSTRINGS); if (ret < 3) { return 0; } @@ -822,6 +828,25 @@ error: return -1; } +#ifdef PCRE_HAVE_JIT_EXEC +static void *DetectPcreThreadInit(void *data /*@unused@*/) +{ + pcre_jit_stack *jit_stack = pcre_jit_stack_alloc(PCRE_JIT_MIN_STACK, PCRE_JIT_MAX_STACK); + + if (jit_stack == NULL) { + SCLogWarning(SC_WARN_PCRE_JITSTACK, "Unable to allocate PCRE JIT stack; will continue without JIT stack"); + } + + return (void *)jit_stack; +} + +static void DetectPcreThreadFree(void *ctx) +{ + if (ctx != NULL) + pcre_jit_stack_free((pcre_jit_stack *)ctx); +} + +#endif static int DetectPcreSetup (DetectEngineCtx *de_ctx, Signature *s, const char *regexstr) { SCEnter(); @@ -840,6 +865,18 @@ static int DetectPcreSetup (DetectEngineCtx *de_ctx, Signature *s, const char *r if (DetectPcreParseCapture(regexstr, de_ctx, pd, capture_names) < 0) goto error; +#ifdef PCRE_HAVE_JIT_EXEC + if (g_jitstack_thread_ctx_id == -1) { + g_jitstack_thread_ctx_id = DetectRegisterThreadCtxFuncs(de_ctx, "jitstack", + DetectPcreThreadInit, (void *) &g_jitstack_thread_ctx_id /* unused */, + DetectPcreThreadFree, 1); + if (g_jitstack_thread_ctx_id == -1) { + SCLogWarning(SC_WARN_REGISTRATION_FAILED, + "Unable to register JIT stack functions. PCRE JIT stack disabled."); + } + } +#endif + int sm_list = -1; if (s->init_data->list != DETECT_SM_LIST_NOTSET) { if (parsed_sm_list != DETECT_SM_LIST_NOTSET && parsed_sm_list != s->init_data->list) { @@ -927,13 +964,9 @@ static void DetectPcreFree(void *ptr) return; DetectPcreData *pd = (DetectPcreData *)ptr; - - if (pd->re != NULL) - pcre_free(pd->re); - if (pd->sd != NULL) - pcre_free_study(pd->sd); - + DetectParseFreeRegex(&pd->parse_regex); SCFree(pd); + return; }