comparison src/core/ngx_regex.c @ 7978:2ca57257252d

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).
author Maxim Dounin <mdounin@mdounin.ru>
date Sat, 25 Dec 2021 01:07:10 +0300
parents f684178faec9
children 060bf88d2473
comparison
equal deleted inserted replaced
7977:336084ff943b 7978:2ca57257252d
8 #include <ngx_config.h> 8 #include <ngx_config.h>
9 #include <ngx_core.h> 9 #include <ngx_core.h>
10 10
11 11
12 typedef struct { 12 typedef struct {
13 ngx_flag_t pcre_jit; 13 ngx_flag_t pcre_jit;
14 ngx_list_t *studies;
14 } ngx_regex_conf_t; 15 } ngx_regex_conf_t;
15 16
16 17
17 static void * ngx_libc_cdecl ngx_regex_malloc(size_t size); 18 static void * ngx_libc_cdecl ngx_regex_malloc(size_t size);
18 static void ngx_libc_cdecl ngx_regex_free(void *p); 19 static void ngx_libc_cdecl ngx_regex_free(void *p);
19 #if (NGX_HAVE_PCRE_JIT) 20 static void ngx_regex_cleanup(void *data);
20 static void ngx_pcre_free_studies(void *data);
21 #endif
22 21
23 static ngx_int_t ngx_regex_module_init(ngx_cycle_t *cycle); 22 static ngx_int_t ngx_regex_module_init(ngx_cycle_t *cycle);
24 23
25 static void *ngx_regex_create_conf(ngx_cycle_t *cycle); 24 static void *ngx_regex_create_conf(ngx_cycle_t *cycle);
26 static char *ngx_regex_init_conf(ngx_cycle_t *cycle, void *conf); 25 static char *ngx_regex_init_conf(ngx_cycle_t *cycle, void *conf);
246 { 245 {
247 return; 246 return;
248 } 247 }
249 248
250 249
250 static void
251 ngx_regex_cleanup(void *data)
252 {
251 #if (NGX_HAVE_PCRE_JIT) 253 #if (NGX_HAVE_PCRE_JIT)
252 254 ngx_regex_conf_t *rcf = data;
253 static void
254 ngx_pcre_free_studies(void *data)
255 {
256 ngx_list_t *studies = data;
257 255
258 ngx_uint_t i; 256 ngx_uint_t i;
259 ngx_list_part_t *part; 257 ngx_list_part_t *part;
260 ngx_regex_elt_t *elts; 258 ngx_regex_elt_t *elts;
261 259
262 part = &studies->part; 260 part = &rcf->studies->part;
263 elts = part->elts; 261 elts = part->elts;
264 262
265 for (i = 0; /* void */ ; i++) { 263 for (i = 0; /* void */ ; i++) {
266 264
267 if (i >= part->nelts) { 265 if (i >= part->nelts) {
271 269
272 part = part->next; 270 part = part->next;
273 elts = part->elts; 271 elts = part->elts;
274 i = 0; 272 i = 0;
275 } 273 }
276
277 if (elts[i].regex->extra != NULL) {
278 pcre_free_study(elts[i].regex->extra);
279 }
280 }
281 }
282
283 #endif
284
285
286 static ngx_int_t
287 ngx_regex_module_init(ngx_cycle_t *cycle)
288 {
289 int opt;
290 const char *errstr;
291 ngx_uint_t i;
292 ngx_list_part_t *part;
293 ngx_regex_elt_t *elts;
294
295 opt = 0;
296
297 #if (NGX_HAVE_PCRE_JIT)
298 {
299 ngx_regex_conf_t *rcf;
300 ngx_pool_cleanup_t *cln;
301
302 rcf = (ngx_regex_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_regex_module);
303
304 if (rcf->pcre_jit) {
305 opt = PCRE_STUDY_JIT_COMPILE;
306 274
307 /* 275 /*
308 * The PCRE JIT compiler uses mmap for its executable codes, so we 276 * The PCRE JIT compiler uses mmap for its executable codes, so we
309 * have to explicitly call the pcre_free_study() function to free 277 * have to explicitly call the pcre_free_study() function to free
310 * this memory. 278 * this memory.
311 */ 279 */
312 280
313 cln = ngx_pool_cleanup_add(cycle->pool, 0); 281 if (elts[i].regex->extra != NULL) {
314 if (cln == NULL) { 282 pcre_free_study(elts[i].regex->extra);
315 return NGX_ERROR; 283 }
316 }
317
318 cln->handler = ngx_pcre_free_studies;
319 cln->data = ngx_pcre_studies;
320 }
321 } 284 }
322 #endif 285 #endif
323 286
287 /*
288 * On configuration parsing errors ngx_regex_module_init() will not
289 * be called. Make sure ngx_pcre_studies is properly cleared anyway.
290 */
291
292 ngx_pcre_studies = NULL;
293 }
294
295
296 static ngx_int_t
297 ngx_regex_module_init(ngx_cycle_t *cycle)
298 {
299 int opt;
300 const char *errstr;
301 ngx_uint_t i;
302 ngx_list_part_t *part;
303 ngx_regex_elt_t *elts;
304 ngx_regex_conf_t *rcf;
305
306 opt = 0;
307
308 rcf = (ngx_regex_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_regex_module);
309
310 #if (NGX_HAVE_PCRE_JIT)
311 if (rcf->pcre_jit) {
312 opt = PCRE_STUDY_JIT_COMPILE;
313 }
314 #endif
315
324 ngx_regex_malloc_init(cycle->pool); 316 ngx_regex_malloc_init(cycle->pool);
325 317
326 part = &ngx_pcre_studies->part; 318 part = &rcf->studies->part;
327 elts = part->elts; 319 elts = part->elts;
328 320
329 for (i = 0; /* void */ ; i++) { 321 for (i = 0; /* void */ ; i++) {
330 322
331 if (i >= part->nelts) { 323 if (i >= part->nelts) {
372 364
373 365
374 static void * 366 static void *
375 ngx_regex_create_conf(ngx_cycle_t *cycle) 367 ngx_regex_create_conf(ngx_cycle_t *cycle)
376 { 368 {
377 ngx_regex_conf_t *rcf; 369 ngx_regex_conf_t *rcf;
370 ngx_pool_cleanup_t *cln;
378 371
379 rcf = ngx_pcalloc(cycle->pool, sizeof(ngx_regex_conf_t)); 372 rcf = ngx_pcalloc(cycle->pool, sizeof(ngx_regex_conf_t));
380 if (rcf == NULL) { 373 if (rcf == NULL) {
381 return NULL; 374 return NULL;
382 } 375 }
383 376
384 rcf->pcre_jit = NGX_CONF_UNSET; 377 rcf->pcre_jit = NGX_CONF_UNSET;
385 378
386 ngx_pcre_studies = ngx_list_create(cycle->pool, 8, sizeof(ngx_regex_elt_t)); 379 cln = ngx_pool_cleanup_add(cycle->pool, 0);
387 if (ngx_pcre_studies == NULL) { 380 if (cln == NULL) {
388 return NULL; 381 return NULL;
389 } 382 }
383
384 cln->handler = ngx_regex_cleanup;
385 cln->data = rcf;
386
387 rcf->studies = ngx_list_create(cycle->pool, 8, sizeof(ngx_regex_elt_t));
388 if (rcf->studies == NULL) {
389 return NULL;
390 }
391
392 ngx_pcre_studies = rcf->studies;
390 393
391 return rcf; 394 return rcf;
392 } 395 }
393 396
394 397