From 09268f58b457dba304e4dc311d98c206d367b971 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Sat, 25 Dec 2021 01:07:10 +0300 Subject: Core: fixed ngx_pcre_studies cleanup. If a configuration parsing fails for some reason, ngx_regex_module_init() is not called, and ngx_pcre_studies remained set despite the fact that the pool it was allocated from is already freed. This might result in a segmentation fault during runtime regular expression compilation, such as in SSI, for example, in the single process mode, or if a worker process died and was respawned from a master process in such an inconsistent state. Fix is to clear ngx_pcre_studies from the pool cleanup handler (which is anyway used to free JIT-compiled patterns). --- src/core/ngx_regex.c | 83 +++++++++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 40 deletions(-) (limited to 'src/core/ngx_regex.c') diff --git a/src/core/ngx_regex.c b/src/core/ngx_regex.c index 52169f655..1e9a1b59e 100644 --- a/src/core/ngx_regex.c +++ b/src/core/ngx_regex.c @@ -10,15 +10,14 @@ typedef struct { - ngx_flag_t pcre_jit; + ngx_flag_t pcre_jit; + ngx_list_t *studies; } ngx_regex_conf_t; static void * ngx_libc_cdecl ngx_regex_malloc(size_t size); static void ngx_libc_cdecl ngx_regex_free(void *p); -#if (NGX_HAVE_PCRE_JIT) -static void ngx_pcre_free_studies(void *data); -#endif +static void ngx_regex_cleanup(void *data); static ngx_int_t ngx_regex_module_init(ngx_cycle_t *cycle); @@ -248,18 +247,17 @@ ngx_regex_free(void *p) } -#if (NGX_HAVE_PCRE_JIT) - static void -ngx_pcre_free_studies(void *data) +ngx_regex_cleanup(void *data) { - ngx_list_t *studies = data; +#if (NGX_HAVE_PCRE_JIT) + ngx_regex_conf_t *rcf = data; ngx_uint_t i; ngx_list_part_t *part; ngx_regex_elt_t *elts; - part = &studies->part; + part = &rcf->studies->part; elts = part->elts; for (i = 0; /* void */ ; i++) { @@ -274,56 +272,50 @@ ngx_pcre_free_studies(void *data) i = 0; } + /* + * The PCRE JIT compiler uses mmap for its executable codes, so we + * have to explicitly call the pcre_free_study() function to free + * this memory. + */ + if (elts[i].regex->extra != NULL) { pcre_free_study(elts[i].regex->extra); } } -} - #endif + /* + * On configuration parsing errors ngx_regex_module_init() will not + * be called. Make sure ngx_pcre_studies is properly cleared anyway. + */ + + ngx_pcre_studies = NULL; +} + static ngx_int_t ngx_regex_module_init(ngx_cycle_t *cycle) { - int opt; - const char *errstr; - ngx_uint_t i; - ngx_list_part_t *part; - ngx_regex_elt_t *elts; + int opt; + const char *errstr; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_regex_elt_t *elts; + ngx_regex_conf_t *rcf; opt = 0; -#if (NGX_HAVE_PCRE_JIT) - { - ngx_regex_conf_t *rcf; - ngx_pool_cleanup_t *cln; - rcf = (ngx_regex_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_regex_module); +#if (NGX_HAVE_PCRE_JIT) if (rcf->pcre_jit) { opt = PCRE_STUDY_JIT_COMPILE; - - /* - * The PCRE JIT compiler uses mmap for its executable codes, so we - * have to explicitly call the pcre_free_study() function to free - * this memory. - */ - - cln = ngx_pool_cleanup_add(cycle->pool, 0); - if (cln == NULL) { - return NGX_ERROR; - } - - cln->handler = ngx_pcre_free_studies; - cln->data = ngx_pcre_studies; - } } #endif ngx_regex_malloc_init(cycle->pool); - part = &ngx_pcre_studies->part; + part = &rcf->studies->part; elts = part->elts; for (i = 0; /* void */ ; i++) { @@ -374,7 +366,8 @@ ngx_regex_module_init(ngx_cycle_t *cycle) static void * ngx_regex_create_conf(ngx_cycle_t *cycle) { - ngx_regex_conf_t *rcf; + ngx_regex_conf_t *rcf; + ngx_pool_cleanup_t *cln; rcf = ngx_pcalloc(cycle->pool, sizeof(ngx_regex_conf_t)); if (rcf == NULL) { @@ -383,11 +376,21 @@ ngx_regex_create_conf(ngx_cycle_t *cycle) rcf->pcre_jit = NGX_CONF_UNSET; - ngx_pcre_studies = ngx_list_create(cycle->pool, 8, sizeof(ngx_regex_elt_t)); - if (ngx_pcre_studies == NULL) { + cln = ngx_pool_cleanup_add(cycle->pool, 0); + if (cln == NULL) { return NULL; } + cln->handler = ngx_regex_cleanup; + cln->data = rcf; + + rcf->studies = ngx_list_create(cycle->pool, 8, sizeof(ngx_regex_elt_t)); + if (rcf->studies == NULL) { + return NULL; + } + + ngx_pcre_studies = rcf->studies; + return rcf; } -- cgit v1.2.3 From 28f8caac37fce6efbeedf75bf5688267be79a0d8 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Sat, 25 Dec 2021 01:07:12 +0300 Subject: Core: ngx_regex.c style cleanup. Notably, ngx_pcre_pool and ngx_pcre_studies are renamed to ngx_regex_pool and ngx_regex_studies, respectively. --- src/core/ngx_regex.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'src/core/ngx_regex.c') diff --git a/src/core/ngx_regex.c b/src/core/ngx_regex.c index 1e9a1b59e..461b4be82 100644 --- a/src/core/ngx_regex.c +++ b/src/core/ngx_regex.c @@ -15,6 +15,9 @@ typedef struct { } ngx_regex_conf_t; +static ngx_inline void ngx_regex_malloc_init(ngx_pool_t *pool); +static ngx_inline void ngx_regex_malloc_done(void); + static void * ngx_libc_cdecl ngx_regex_malloc(size_t size); static void ngx_libc_cdecl ngx_regex_free(void *p); static void ngx_regex_cleanup(void *data); @@ -64,8 +67,8 @@ ngx_module_t ngx_regex_module = { }; -static ngx_pool_t *ngx_pcre_pool; -static ngx_list_t *ngx_pcre_studies; +static ngx_pool_t *ngx_regex_pool; +static ngx_list_t *ngx_regex_studies; void @@ -79,14 +82,14 @@ ngx_regex_init(void) static ngx_inline void ngx_regex_malloc_init(ngx_pool_t *pool) { - ngx_pcre_pool = pool; + ngx_regex_pool = pool; } static ngx_inline void ngx_regex_malloc_done(void) { - ngx_pcre_pool = NULL; + ngx_regex_pool = NULL; } @@ -112,13 +115,13 @@ ngx_regex_compile(ngx_regex_compile_t *rc) rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, "pcre_compile() failed: %s in \"%V\"", errstr, &rc->pattern) - - rc->err.data; + - rc->err.data; } else { rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, "pcre_compile() failed: %s in \"%V\" at \"%s\"", errstr, &rc->pattern, rc->pattern.data + erroff) - - rc->err.data; + - rc->err.data; } return NGX_ERROR; @@ -133,8 +136,8 @@ ngx_regex_compile(ngx_regex_compile_t *rc) /* do not study at runtime */ - if (ngx_pcre_studies != NULL) { - elt = ngx_list_push(ngx_pcre_studies); + if (ngx_regex_studies != NULL) { + elt = ngx_list_push(ngx_regex_studies); if (elt == NULL) { goto nomem; } @@ -229,11 +232,8 @@ ngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log) static void * ngx_libc_cdecl ngx_regex_malloc(size_t size) { - ngx_pool_t *pool; - pool = ngx_pcre_pool; - - if (pool) { - return ngx_palloc(pool, size); + if (ngx_regex_pool) { + return ngx_palloc(ngx_regex_pool, size); } return NULL; @@ -286,10 +286,10 @@ ngx_regex_cleanup(void *data) /* * On configuration parsing errors ngx_regex_module_init() will not - * be called. Make sure ngx_pcre_studies is properly cleared anyway. + * be called. Make sure ngx_regex_studies is properly cleared anyway. */ - ngx_pcre_studies = NULL; + ngx_regex_studies = NULL; } @@ -357,7 +357,7 @@ ngx_regex_module_init(ngx_cycle_t *cycle) ngx_regex_malloc_done(); - ngx_pcre_studies = NULL; + ngx_regex_studies = NULL; return NGX_OK; } @@ -389,7 +389,7 @@ ngx_regex_create_conf(ngx_cycle_t *cycle) return NULL; } - ngx_pcre_studies = rcf->studies; + ngx_regex_studies = rcf->studies; return rcf; } -- cgit v1.2.3 From c6fec0b027569a4e0b1d8aaee7dea0f2e4d6052b Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Sat, 25 Dec 2021 01:07:15 +0300 Subject: PCRE2 library support. The PCRE2 library is now used by default if found, instead of the original PCRE library. If needed for some reason, this can be disabled with the --without-pcre2 configure option. To make it possible to specify paths to the library and include files via --with-cc-opt / --with-ld-opt, the library is first tested without any additional paths and options. If this fails, the pcre2-config script is used. Similarly to the original PCRE library, it is now possible to build PCRE2 from sources with nginx configure, by using the --with-pcre= option. It automatically detects if PCRE or PCRE2 sources are provided. Note that compiling PCRE2 10.33 and later requires inttypes.h. When compiling on Windows with MSVC, inttypes.h is only available starting with MSVC 2013. In older versions some replacement needs to be provided ("echo '#include ' > pcre2-10.xx/src/inttypes.h" is good enough for MSVC 2010). The interface on nginx side remains unchanged. --- src/core/ngx_regex.c | 331 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 325 insertions(+), 6 deletions(-) (limited to 'src/core/ngx_regex.c') diff --git a/src/core/ngx_regex.c b/src/core/ngx_regex.c index 461b4be82..5ae4f780b 100644 --- a/src/core/ngx_regex.c +++ b/src/core/ngx_regex.c @@ -18,8 +18,13 @@ typedef struct { static ngx_inline void ngx_regex_malloc_init(ngx_pool_t *pool); static ngx_inline void ngx_regex_malloc_done(void); +#if (NGX_PCRE2) +static void * ngx_libc_cdecl ngx_regex_malloc(size_t size, void *data); +static void ngx_libc_cdecl ngx_regex_free(void *p, void *data); +#else static void * ngx_libc_cdecl ngx_regex_malloc(size_t size); static void ngx_libc_cdecl ngx_regex_free(void *p); +#endif static void ngx_regex_cleanup(void *data); static ngx_int_t ngx_regex_module_init(ngx_cycle_t *cycle); @@ -67,15 +72,24 @@ ngx_module_t ngx_regex_module = { }; -static ngx_pool_t *ngx_regex_pool; -static ngx_list_t *ngx_regex_studies; +static ngx_pool_t *ngx_regex_pool; +static ngx_list_t *ngx_regex_studies; +static ngx_uint_t ngx_regex_direct_alloc; + +#if (NGX_PCRE2) +static pcre2_compile_context *ngx_regex_compile_context; +static pcre2_match_data *ngx_regex_match_data; +static ngx_uint_t ngx_regex_match_data_size; +#endif void ngx_regex_init(void) { +#if !(NGX_PCRE2) pcre_malloc = ngx_regex_malloc; pcre_free = ngx_regex_free; +#endif } @@ -83,6 +97,7 @@ static ngx_inline void ngx_regex_malloc_init(ngx_pool_t *pool) { ngx_regex_pool = pool; + ngx_regex_direct_alloc = (pool == NULL) ? 1 : 0; } @@ -90,9 +105,146 @@ static ngx_inline void ngx_regex_malloc_done(void) { ngx_regex_pool = NULL; + ngx_regex_direct_alloc = 0; } +#if (NGX_PCRE2) + +ngx_int_t +ngx_regex_compile(ngx_regex_compile_t *rc) +{ + int n, errcode; + char *p; + u_char errstr[128]; + size_t erroff; + pcre2_code *re; + ngx_regex_elt_t *elt; + pcre2_general_context *gctx; + pcre2_compile_context *cctx; + + if (ngx_regex_compile_context == NULL) { + /* + * Allocate a compile context if not yet allocated. This uses + * direct allocations from heap, so the result can be cached + * even at runtime. + */ + + ngx_regex_malloc_init(NULL); + + gctx = pcre2_general_context_create(ngx_regex_malloc, ngx_regex_free, + NULL); + if (gctx == NULL) { + ngx_regex_malloc_done(); + goto nomem; + } + + cctx = pcre2_compile_context_create(gctx); + if (cctx == NULL) { + pcre2_general_context_free(gctx); + ngx_regex_malloc_done(); + goto nomem; + } + + ngx_regex_compile_context = cctx; + + pcre2_general_context_free(gctx); + ngx_regex_malloc_done(); + } + + ngx_regex_malloc_init(rc->pool); + + re = pcre2_compile(rc->pattern.data, rc->pattern.len, + (uint32_t) rc->options, &errcode, &erroff, + ngx_regex_compile_context); + + /* ensure that there is no current pool */ + ngx_regex_malloc_done(); + + if (re == NULL) { + pcre2_get_error_message(errcode, errstr, 128); + + if ((size_t) erroff == rc->pattern.len) { + rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, + "pcre2_compile() failed: %s in \"%V\"", + errstr, &rc->pattern) + - rc->err.data; + + } else { + rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, + "pcre2_compile() failed: %s in \"%V\" at \"%s\"", + errstr, &rc->pattern, rc->pattern.data + erroff) + - rc->err.data; + } + + return NGX_ERROR; + } + + rc->regex = re; + + /* do not study at runtime */ + + if (ngx_regex_studies != NULL) { + elt = ngx_list_push(ngx_regex_studies); + if (elt == NULL) { + goto nomem; + } + + elt->regex = rc->regex; + elt->name = rc->pattern.data; + } + + n = pcre2_pattern_info(re, PCRE2_INFO_CAPTURECOUNT, &rc->captures); + if (n < 0) { + p = "pcre2_pattern_info(\"%V\", PCRE2_INFO_CAPTURECOUNT) failed: %d"; + goto failed; + } + + if (rc->captures == 0) { + return NGX_OK; + } + + n = pcre2_pattern_info(re, PCRE2_INFO_NAMECOUNT, &rc->named_captures); + if (n < 0) { + p = "pcre2_pattern_info(\"%V\", PCRE2_INFO_NAMECOUNT) failed: %d"; + goto failed; + } + + if (rc->named_captures == 0) { + return NGX_OK; + } + + n = pcre2_pattern_info(re, PCRE2_INFO_NAMEENTRYSIZE, &rc->name_size); + if (n < 0) { + p = "pcre2_pattern_info(\"%V\", PCRE2_INFO_NAMEENTRYSIZE) failed: %d"; + goto failed; + } + + n = pcre2_pattern_info(re, PCRE2_INFO_NAMETABLE, &rc->names); + if (n < 0) { + p = "pcre2_pattern_info(\"%V\", PCRE2_INFO_NAMETABLE) failed: %d"; + goto failed; + } + + return NGX_OK; + +failed: + + rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, p, &rc->pattern, n) + - rc->err.data; + return NGX_ERROR; + +nomem: + + rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, + "regex \"%V\" compilation failed: no memory", + &rc->pattern) + - rc->err.data; + return NGX_ERROR; +} + +#else + ngx_int_t ngx_regex_compile(ngx_regex_compile_t *rc) { @@ -195,6 +347,74 @@ nomem: return NGX_ERROR; } +#endif + + +#if (NGX_PCRE2) + +ngx_int_t +ngx_regex_exec(ngx_regex_t *re, ngx_str_t *s, int *captures, ngx_uint_t size) +{ + size_t *ov; + ngx_int_t rc; + ngx_uint_t n, i; + + /* + * The pcre2_match() function might allocate memory for backtracking + * frames, typical allocations are from 40k and above. So the allocator + * is configured to do direct allocations from heap during matching. + */ + + ngx_regex_malloc_init(NULL); + + if (ngx_regex_match_data == NULL + || size > ngx_regex_match_data_size) + { + /* + * Allocate a match data if not yet allocated or smaller than + * needed. + */ + + if (ngx_regex_match_data) { + pcre2_match_data_free(ngx_regex_match_data); + } + + ngx_regex_match_data_size = size; + ngx_regex_match_data = pcre2_match_data_create(size / 3, NULL); + + if (ngx_regex_match_data == NULL) { + rc = PCRE2_ERROR_NOMEMORY; + goto failed; + } + } + + rc = pcre2_match(re, s->data, s->len, 0, 0, ngx_regex_match_data, NULL); + + if (rc < 0) { + goto failed; + } + + n = pcre2_get_ovector_count(ngx_regex_match_data); + ov = pcre2_get_ovector_pointer(ngx_regex_match_data); + + if (n > size / 3) { + n = size / 3; + } + + for (i = 0; i < n; i++) { + captures[i * 2] = ov[i * 2]; + captures[i * 2 + 1] = ov[i * 2 + 1]; + } + +failed: + + ngx_regex_malloc_done(); + + return rc; +} + +#endif + ngx_int_t ngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log) @@ -229,6 +449,35 @@ ngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log) } +#if (NGX_PCRE2) + +static void * ngx_libc_cdecl +ngx_regex_malloc(size_t size, void *data) +{ + if (ngx_regex_pool) { + return ngx_palloc(ngx_regex_pool, size); + } + + if (ngx_regex_direct_alloc) { + return ngx_alloc(size, ngx_cycle->log); + } + + return NULL; +} + + +static void ngx_libc_cdecl +ngx_regex_free(void *p, void *data) +{ + if (ngx_regex_direct_alloc) { + ngx_free(p); + } + + return; +} + +#else + static void * ngx_libc_cdecl ngx_regex_malloc(size_t size) { @@ -246,11 +495,13 @@ ngx_regex_free(void *p) return; } +#endif + static void ngx_regex_cleanup(void *data) { -#if (NGX_HAVE_PCRE_JIT) +#if (NGX_PCRE2 || NGX_HAVE_PCRE_JIT) ngx_regex_conf_t *rcf = data; ngx_uint_t i; @@ -275,12 +526,17 @@ ngx_regex_cleanup(void *data) /* * The PCRE JIT compiler uses mmap for its executable codes, so we * have to explicitly call the pcre_free_study() function to free - * this memory. + * this memory. In PCRE2, we call the pcre2_code_free() function + * for the same reason. */ +#if (NGX_PCRE2) + pcre2_code_free(elts[i].regex); +#else if (elts[i].regex->extra != NULL) { pcre_free_study(elts[i].regex->extra); } +#endif } #endif @@ -290,6 +546,26 @@ ngx_regex_cleanup(void *data) */ ngx_regex_studies = NULL; + +#if (NGX_PCRE2) + + /* + * Free compile context and match data. If needed at runtime by + * the new cycle, these will be re-allocated. + */ + + if (ngx_regex_compile_context) { + pcre2_compile_context_free(ngx_regex_compile_context); + ngx_regex_compile_context = NULL; + } + + if (ngx_regex_match_data) { + pcre2_match_data_free(ngx_regex_match_data); + ngx_regex_match_data = NULL; + ngx_regex_match_data_size = 0; + } + +#endif } @@ -297,7 +573,9 @@ static ngx_int_t ngx_regex_module_init(ngx_cycle_t *cycle) { int opt; +#if !(NGX_PCRE2) const char *errstr; +#endif ngx_uint_t i; ngx_list_part_t *part; ngx_regex_elt_t *elts; @@ -307,10 +585,16 @@ ngx_regex_module_init(ngx_cycle_t *cycle) rcf = (ngx_regex_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_regex_module); -#if (NGX_HAVE_PCRE_JIT) +#if (NGX_PCRE2 || NGX_HAVE_PCRE_JIT) + if (rcf->pcre_jit) { +#if (NGX_PCRE2) + opt = 1; +#else opt = PCRE_STUDY_JIT_COMPILE; +#endif } + #endif ngx_regex_malloc_init(cycle->pool); @@ -330,6 +614,23 @@ ngx_regex_module_init(ngx_cycle_t *cycle) i = 0; } +#if (NGX_PCRE2) + + if (opt) { + int n; + + n = pcre2_jit_compile(elts[i].regex, PCRE2_JIT_COMPLETE); + + if (n != 0) { + ngx_log_error(NGX_LOG_INFO, cycle->log, 0, + "pcre2_jit_compile() failed: %d in \"%s\", " + "ignored", + n, elts[i].name); + } + } + +#else + elts[i].regex->extra = pcre_study(elts[i].regex->code, opt, &errstr); if (errstr != NULL) { @@ -352,12 +653,16 @@ ngx_regex_module_init(ngx_cycle_t *cycle) elts[i].name); } } +#endif #endif } ngx_regex_malloc_done(); ngx_regex_studies = NULL; +#if (NGX_PCRE2) + ngx_regex_compile_context = NULL; +#endif return NGX_OK; } @@ -415,7 +720,21 @@ ngx_regex_pcre_jit(ngx_conf_t *cf, void *post, void *data) return NGX_CONF_OK; } -#if (NGX_HAVE_PCRE_JIT) +#if (NGX_PCRE2) + { + int r; + uint32_t jit; + + jit = 0; + r = pcre2_config(PCRE2_CONFIG_JIT, &jit); + + if (r != 0 || jit != 1) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "PCRE2 library does not support JIT"); + *fp = 0; + } + } +#elif (NGX_HAVE_PCRE_JIT) { int jit, r; -- cgit v1.2.3 From 931acbf5bcd550af8613d131f4ba49e22e909efb Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Sat, 25 Dec 2021 01:07:16 +0300 Subject: PCRE2 and PCRE binary compatibility. With this change, dynamic modules using nginx regex interface can be used regardless of the variant of the PCRE library nginx was compiled with. If a module is compiled with different PCRE library variant, in case of ngx_regex_exec() errors it will report wrong function name in error messages. This is believed to be tolerable, given that fixing this will require interface changes. --- src/core/ngx_regex.c | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) (limited to 'src/core/ngx_regex.c') diff --git a/src/core/ngx_regex.c b/src/core/ngx_regex.c index 5ae4f780b..991728b27 100644 --- a/src/core/ngx_regex.c +++ b/src/core/ngx_regex.c @@ -118,6 +118,7 @@ ngx_regex_compile(ngx_regex_compile_t *rc) char *p; u_char errstr[128]; size_t erroff; + uint32_t options; pcre2_code *re; ngx_regex_elt_t *elt; pcre2_general_context *gctx; @@ -152,11 +153,24 @@ ngx_regex_compile(ngx_regex_compile_t *rc) ngx_regex_malloc_done(); } + options = 0; + + if (rc->options & NGX_REGEX_CASELESS) { + options |= PCRE2_CASELESS; + } + + if (rc->options & ~NGX_REGEX_CASELESS) { + rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, + "regex \"%V\" compilation failed: invalid options", + &rc->pattern) + - rc->err.data; + return NGX_ERROR; + } + ngx_regex_malloc_init(rc->pool); - re = pcre2_compile(rc->pattern.data, rc->pattern.len, - (uint32_t) rc->options, &errcode, &erroff, - ngx_regex_compile_context); + re = pcre2_compile(rc->pattern.data, rc->pattern.len, options, + &errcode, &erroff, ngx_regex_compile_context); /* ensure that there is no current pool */ ngx_regex_malloc_done(); @@ -252,11 +266,26 @@ ngx_regex_compile(ngx_regex_compile_t *rc) char *p; pcre *re; const char *errstr; + ngx_uint_t options; ngx_regex_elt_t *elt; + options = 0; + + if (rc->options & NGX_REGEX_CASELESS) { + options |= PCRE_CASELESS; + } + + if (rc->options & ~NGX_REGEX_CASELESS) { + rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, + "regex \"%V\" compilation failed: invalid options", + &rc->pattern) + - rc->err.data; + return NGX_ERROR; + } + ngx_regex_malloc_init(rc->pool); - re = pcre_compile((const char *) rc->pattern.data, (int) rc->options, + re = pcre_compile((const char *) rc->pattern.data, (int) options, &errstr, &erroff, NULL); /* ensure that there is no current pool */ @@ -413,6 +442,15 @@ failed: return rc; } +#else + +ngx_int_t +ngx_regex_exec(ngx_regex_t *re, ngx_str_t *s, int *captures, ngx_uint_t size) +{ + return pcre_exec(re->code, re->extra, (const char *) s->data, s->len, + 0, 0, captures, size); +} + #endif -- cgit v1.2.3 From d5f1f169bc71d32b96960266d54e189c69af00ba Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Sat, 25 Dec 2021 01:07:18 +0300 Subject: Core: added NGX_REGEX_MULTILINE for 3rd party modules. Notably, NAXSI is known to misuse ngx_regex_compile() with rc.options set to PCRE_CASELESS | PCRE_MULTILINE. With PCRE2 support, and notably binary compatibility changes, it is no longer possible to set PCRE[2]_MULTILINE option without using proper interface. To facilitate correct usage, this change adds the NGX_REGEX_MULTILINE option. --- src/core/ngx_regex.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src/core/ngx_regex.c') diff --git a/src/core/ngx_regex.c b/src/core/ngx_regex.c index 991728b27..bebf3b6a8 100644 --- a/src/core/ngx_regex.c +++ b/src/core/ngx_regex.c @@ -159,7 +159,11 @@ ngx_regex_compile(ngx_regex_compile_t *rc) options |= PCRE2_CASELESS; } - if (rc->options & ~NGX_REGEX_CASELESS) { + if (rc->options & NGX_REGEX_MULTILINE) { + options |= PCRE2_MULTILINE; + } + + if (rc->options & ~(NGX_REGEX_CASELESS|NGX_REGEX_MULTILINE)) { rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, "regex \"%V\" compilation failed: invalid options", &rc->pattern) @@ -275,7 +279,11 @@ ngx_regex_compile(ngx_regex_compile_t *rc) options |= PCRE_CASELESS; } - if (rc->options & ~NGX_REGEX_CASELESS) { + if (rc->options & NGX_REGEX_MULTILINE) { + options |= PCRE_MULTILINE; + } + + if (rc->options & ~(NGX_REGEX_CASELESS|NGX_REGEX_MULTILINE)) { rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, "regex \"%V\" compilation failed: invalid options", &rc->pattern) -- cgit v1.2.3