Skip to content

Commit 0e8ff57

Browse files
author
Istvan Miklos
committed
Add RegExp recursion depth limit
The regexp engine does not have any recursion depth check, thus it can cause problems with various regexps. Added a new build option `--regexp-recursion-limit N` whose default value is 1000. For unlimited recursion depth use 0. Also added a build-option-test for the unlimited recursion depth. Fixes #2448 JerryScript-DCO-1.0-Signed-off-by: Istvan Miklos imiklos2@inf.u-szeged.hu
1 parent 9ab33e8 commit 0e8ff57

File tree

7 files changed

+147
-1
lines changed

7 files changed

+147
-1
lines changed

jerry-core/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ message(STATUS "FEATURE_SYSTEM_ALLOCATOR " ${FEATURE_SYSTEM_ALLOCATOR})
9494
message(STATUS "FEATURE_VALGRIND " ${FEATURE_VALGRIND})
9595
message(STATUS "FEATURE_VM_EXEC_STOP " ${FEATURE_VM_EXEC_STOP})
9696
message(STATUS "MEM_HEAP_SIZE_KB " ${MEM_HEAP_SIZE_KB})
97+
message(STATUS "REGEXP_RECURSION_LIMIT " ${REGEXP_RECURSION_LIMIT_N})
9798

9899
# Include directories
99100
set(INCLUDE_CORE_PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
@@ -242,6 +243,11 @@ if(FEATURE_REGEXP_STRICT_MODE)
242243
set(DEFINES_JERRY ${DEFINES_JERRY} ENABLE_REGEXP_STRICT_MODE)
243244
endif()
244245

246+
# RegExp recursion depth limit
247+
if(REGEXP_RECURSION_LIMIT_N)
248+
set(DEFINES_JERRY ${DEFINES_JERRY} REGEXP_RECURSION_LIMIT=${REGEXP_RECURSION_LIMIT_N})
249+
endif()
250+
245251
# RegExp byte-code dumps
246252
if(FEATURE_REGEXP_DUMP)
247253
set(DEFINES_JERRY ${DEFINES_JERRY} REGEXP_DUMP_BYTE_CODE)

jerry-core/ecma/operations/ecma-regexp-object.c

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,14 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
364364
const lit_utf8_byte_t *str_p, /**< input string pointer */
365365
const lit_utf8_byte_t **out_str_p) /**< [out] matching substring iterator */
366366
{
367+
#ifdef REGEXP_RECURSION_LIMIT
368+
if (re_ctx_p->recursion_depth >= REGEXP_RECURSION_LIMIT)
369+
{
370+
ecma_value_t ret_value = ecma_raise_range_error ("RegExp executor recursion limit is exceeded.");
371+
return ret_value;
372+
}
373+
++re_ctx_p->recursion_depth;
374+
#endif /* REGEXP_RECURSION_LIMIT */
367375
const lit_utf8_byte_t *str_curr_p = str_p;
368376

369377
while (true)
@@ -376,12 +384,18 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
376384
{
377385
JERRY_TRACE_MSG ("Execute RE_OP_MATCH: match\n");
378386
*out_str_p = str_curr_p;
387+
#ifdef REGEXP_RECURSION_LIMIT
388+
--re_ctx_p->recursion_depth;
389+
#endif /* REGEXP_RECURSION_LIMIT */
379390
return ECMA_VALUE_TRUE; /* match */
380391
}
381392
case RE_OP_CHAR:
382393
{
383394
if (str_curr_p >= re_ctx_p->input_end_p)
384395
{
396+
#ifdef REGEXP_RECURSION_LIMIT
397+
--re_ctx_p->recursion_depth;
398+
#endif /* REGEXP_RECURSION_LIMIT */
385399
return ECMA_VALUE_FALSE; /* fail */
386400
}
387401

@@ -393,6 +407,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
393407
if (ch1 != ch2)
394408
{
395409
JERRY_TRACE_MSG ("fail\n");
410+
#ifdef REGEXP_RECURSION_LIMIT
411+
--re_ctx_p->recursion_depth;
412+
#endif /* REGEXP_RECURSION_LIMIT */
396413
return ECMA_VALUE_FALSE; /* fail */
397414
}
398415

@@ -404,6 +421,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
404421
{
405422
if (str_curr_p >= re_ctx_p->input_end_p)
406423
{
424+
#ifdef REGEXP_RECURSION_LIMIT
425+
--re_ctx_p->recursion_depth;
426+
#endif /* REGEXP_RECURSION_LIMIT */
407427
return ECMA_VALUE_FALSE; /* fail */
408428
}
409429

@@ -413,6 +433,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
413433
if (lit_char_is_line_terminator (ch))
414434
{
415435
JERRY_TRACE_MSG ("fail\n");
436+
#ifdef REGEXP_RECURSION_LIMIT
437+
--re_ctx_p->recursion_depth;
438+
#endif /* REGEXP_RECURSION_LIMIT */
416439
return ECMA_VALUE_FALSE; /* fail */
417440
}
418441

@@ -432,6 +455,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
432455
if (!(re_ctx_p->flags & RE_FLAG_MULTILINE))
433456
{
434457
JERRY_TRACE_MSG ("fail\n");
458+
#ifdef REGEXP_RECURSION_LIMIT
459+
--re_ctx_p->recursion_depth;
460+
#endif /* REGEXP_RECURSION_LIMIT */
435461
return ECMA_VALUE_FALSE; /* fail */
436462
}
437463

@@ -442,6 +468,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
442468
}
443469

444470
JERRY_TRACE_MSG ("fail\n");
471+
#ifdef REGEXP_RECURSION_LIMIT
472+
--re_ctx_p->recursion_depth;
473+
#endif /* REGEXP_RECURSION_LIMIT */
445474
return ECMA_VALUE_FALSE; /* fail */
446475
}
447476
case RE_OP_ASSERT_END:
@@ -457,6 +486,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
457486
if (!(re_ctx_p->flags & RE_FLAG_MULTILINE))
458487
{
459488
JERRY_TRACE_MSG ("fail\n");
489+
#ifdef REGEXP_RECURSION_LIMIT
490+
--re_ctx_p->recursion_depth;
491+
#endif /* REGEXP_RECURSION_LIMIT */
460492
return ECMA_VALUE_FALSE; /* fail */
461493
}
462494

@@ -467,6 +499,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
467499
}
468500

469501
JERRY_TRACE_MSG ("fail\n");
502+
#ifdef REGEXP_RECURSION_LIMIT
503+
--re_ctx_p->recursion_depth;
504+
#endif /* REGEXP_RECURSION_LIMIT */
470505
return ECMA_VALUE_FALSE; /* fail */
471506
}
472507
case RE_OP_ASSERT_WORD_BOUNDARY:
@@ -498,6 +533,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
498533
if (is_wordchar_left == is_wordchar_right)
499534
{
500535
JERRY_TRACE_MSG ("fail\n");
536+
#ifdef REGEXP_RECURSION_LIMIT
537+
--re_ctx_p->recursion_depth;
538+
#endif /* REGEXP_RECURSION_LIMIT */
501539
return ECMA_VALUE_FALSE; /* fail */
502540
}
503541
}
@@ -509,6 +547,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
509547
if (is_wordchar_left != is_wordchar_right)
510548
{
511549
JERRY_TRACE_MSG ("fail\n");
550+
#ifdef REGEXP_RECURSION_LIMIT
551+
--re_ctx_p->recursion_depth;
552+
#endif /* REGEXP_RECURSION_LIMIT */
512553
return ECMA_VALUE_FALSE; /* fail */
513554
}
514555
}
@@ -563,6 +604,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
563604

564605
if (!ECMA_IS_VALUE_ERROR (match_value))
565606
{
607+
#ifdef REGEXP_RECURSION_LIMIT
608+
--re_ctx_p->recursion_depth;
609+
#endif /* REGEXP_RECURSION_LIMIT */
566610
if (ecma_is_value_true (match_value))
567611
{
568612
*out_str_p = sub_str_p;
@@ -588,6 +632,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
588632
if (str_curr_p >= re_ctx_p->input_end_p)
589633
{
590634
JERRY_TRACE_MSG ("fail\n");
635+
#ifdef REGEXP_RECURSION_LIMIT
636+
--re_ctx_p->recursion_depth;
637+
#endif /* REGEXP_RECURSION_LIMIT */
591638
return ECMA_VALUE_FALSE; /* fail */
592639
}
593640

@@ -618,6 +665,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
618665
if (!is_match)
619666
{
620667
JERRY_TRACE_MSG ("fail\n");
668+
#ifdef REGEXP_RECURSION_LIMIT
669+
--re_ctx_p->recursion_depth;
670+
#endif /* REGEXP_RECURSION_LIMIT */
621671
return ECMA_VALUE_FALSE; /* fail */
622672
}
623673
}
@@ -627,6 +677,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
627677
if (is_match)
628678
{
629679
JERRY_TRACE_MSG ("fail\n");
680+
#ifdef REGEXP_RECURSION_LIMIT
681+
--re_ctx_p->recursion_depth;
682+
#endif /* REGEXP_RECURSION_LIMIT */
630683
return ECMA_VALUE_FALSE; /* fail */
631684
}
632685
}
@@ -657,6 +710,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
657710
if (str_curr_p >= re_ctx_p->input_end_p)
658711
{
659712
JERRY_TRACE_MSG ("fail\n");
713+
#ifdef REGEXP_RECURSION_LIMIT
714+
--re_ctx_p->recursion_depth;
715+
#endif /* REGEXP_RECURSION_LIMIT */
660716
return ECMA_VALUE_FALSE; /* fail */
661717
}
662718

@@ -666,6 +722,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
666722
if (ch1 != ch2)
667723
{
668724
JERRY_TRACE_MSG ("fail\n");
725+
#ifdef REGEXP_RECURSION_LIMIT
726+
--re_ctx_p->recursion_depth;
727+
#endif /* REGEXP_RECURSION_LIMIT */
669728
return ECMA_VALUE_FALSE; /* fail */
670729
}
671730
}
@@ -689,6 +748,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
689748
if (ecma_is_value_true (match_value))
690749
{
691750
*out_str_p = sub_str_p;
751+
#ifdef REGEXP_RECURSION_LIMIT
752+
--re_ctx_p->recursion_depth;
753+
#endif /* REGEXP_RECURSION_LIMIT */
692754
return match_value; /* match */
693755
}
694756
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -703,13 +765,19 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
703765
bc_p = old_bc_p;
704766

705767
re_ctx_p->saved_p[RE_GLOBAL_START_IDX] = old_start_p;
768+
#ifdef REGEXP_RECURSION_LIMIT
769+
--re_ctx_p->recursion_depth;
770+
#endif /* REGEXP_RECURSION_LIMIT */
706771
return ECMA_VALUE_FALSE; /* fail */
707772
}
708773
case RE_OP_SAVE_AND_MATCH:
709774
{
710775
JERRY_TRACE_MSG ("End of pattern is reached: match\n");
711776
re_ctx_p->saved_p[RE_GLOBAL_END_IDX] = str_curr_p;
712777
*out_str_p = str_curr_p;
778+
#ifdef REGEXP_RECURSION_LIMIT
779+
--re_ctx_p->recursion_depth;
780+
#endif /* REGEXP_RECURSION_LIMIT */
713781
return ECMA_VALUE_TRUE; /* match */
714782
}
715783
case RE_OP_ALTERNATIVE:
@@ -774,6 +842,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
774842
if (ecma_is_value_true (match_value))
775843
{
776844
*out_str_p = sub_str_p;
845+
#ifdef REGEXP_RECURSION_LIMIT
846+
--re_ctx_p->recursion_depth;
847+
#endif /* REGEXP_RECURSION_LIMIT */
777848
return match_value; /* match */
778849
}
779850
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -832,6 +903,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
832903
if (ecma_is_value_true (match_value))
833904
{
834905
*out_str_p = sub_str_p;
906+
#ifdef REGEXP_RECURSION_LIMIT
907+
--re_ctx_p->recursion_depth;
908+
#endif /* REGEXP_RECURSION_LIMIT */
835909
return match_value; /* match */
836910
}
837911
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -856,6 +930,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
856930
if (ecma_is_value_true (match_value))
857931
{
858932
*out_str_p = sub_str_p;
933+
#ifdef REGEXP_RECURSION_LIMIT
934+
--re_ctx_p->recursion_depth;
935+
#endif /* REGEXP_RECURSION_LIMIT */
859936
return match_value; /* match */
860937
}
861938
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -865,6 +942,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
865942
}
866943

867944
re_ctx_p->saved_p[start_idx] = old_start_p;
945+
#ifdef REGEXP_RECURSION_LIMIT
946+
--re_ctx_p->recursion_depth;
947+
#endif /* REGEXP_RECURSION_LIMIT */
868948
return ECMA_VALUE_FALSE; /* fail */
869949
}
870950
case RE_OP_CAPTURE_NON_GREEDY_GROUP_END:
@@ -910,6 +990,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
910990
if (ecma_is_value_true (match_value))
911991
{
912992
*out_str_p = sub_str_p;
993+
#ifdef REGEXP_RECURSION_LIMIT
994+
--re_ctx_p->recursion_depth;
995+
#endif /* REGEXP_RECURSION_LIMIT */
913996
return match_value; /* match */
914997
}
915998
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -958,6 +1041,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
9581041
if (re_ctx_p->num_of_iterations_p[iter_idx] >= min
9591042
&& str_curr_p== re_ctx_p->saved_p[start_idx])
9601043
{
1044+
#ifdef REGEXP_RECURSION_LIMIT
1045+
--re_ctx_p->recursion_depth;
1046+
#endif /* REGEXP_RECURSION_LIMIT */
9611047
return ECMA_VALUE_FALSE; /* fail */
9621048
}
9631049

@@ -979,6 +1065,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
9791065
if (ecma_is_value_true (match_value))
9801066
{
9811067
*out_str_p = sub_str_p;
1068+
#ifdef REGEXP_RECURSION_LIMIT
1069+
--re_ctx_p->recursion_depth;
1070+
#endif /* REGEXP_RECURSION_LIMIT */
9821071
return match_value; /* match */
9831072
}
9841073
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -1003,6 +1092,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
10031092
if (ecma_is_value_true (match_value))
10041093
{
10051094
*out_str_p = sub_str_p;
1095+
#ifdef REGEXP_RECURSION_LIMIT
1096+
--re_ctx_p->recursion_depth;
1097+
#endif /* REGEXP_RECURSION_LIMIT */
10061098
return match_value; /* match */
10071099
}
10081100
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -1024,6 +1116,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
10241116
if (ecma_is_value_true (match_value))
10251117
{
10261118
*out_str_p = sub_str_p;
1119+
#ifdef REGEXP_RECURSION_LIMIT
1120+
--re_ctx_p->recursion_depth;
1121+
#endif /* REGEXP_RECURSION_LIMIT */
10271122
return match_value; /* match */
10281123
}
10291124
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -1035,6 +1130,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
10351130
/* restore if fails */
10361131
re_ctx_p->saved_p[end_idx] = old_end_p;
10371132
re_ctx_p->num_of_iterations_p[iter_idx]--;
1133+
#ifdef REGEXP_RECURSION_LIMIT
1134+
--re_ctx_p->recursion_depth;
1135+
#endif /* REGEXP_RECURSION_LIMIT */
10381136
return ECMA_VALUE_FALSE; /* fail */
10391137
}
10401138
case RE_OP_NON_GREEDY_ITERATOR:
@@ -1059,6 +1157,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
10591157
if (ecma_is_value_true (match_value))
10601158
{
10611159
*out_str_p = sub_str_p;
1160+
#ifdef REGEXP_RECURSION_LIMIT
1161+
--re_ctx_p->recursion_depth;
1162+
#endif /* REGEXP_RECURSION_LIMIT */
10621163
return match_value; /* match */
10631164
}
10641165
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -1082,6 +1183,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
10821183
str_curr_p = sub_str_p;
10831184
num_of_iter++;
10841185
}
1186+
#ifdef REGEXP_RECURSION_LIMIT
1187+
--re_ctx_p->recursion_depth;
1188+
#endif /* REGEXP_RECURSION_LIMIT */
10851189
return ECMA_VALUE_FALSE; /* fail */
10861190
}
10871191
default:
@@ -1125,6 +1229,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
11251229
if (ecma_is_value_true (match_value))
11261230
{
11271231
*out_str_p = sub_str_p;
1232+
#ifdef REGEXP_RECURSION_LIMIT
1233+
--re_ctx_p->recursion_depth;
1234+
#endif /* REGEXP_RECURSION_LIMIT */
11281235
return match_value; /* match */
11291236
}
11301237
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -1140,6 +1247,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
11401247
lit_utf8_read_prev (&str_curr_p);
11411248
num_of_iter--;
11421249
}
1250+
#ifdef REGEXP_RECURSION_LIMIT
1251+
--re_ctx_p->recursion_depth;
1252+
#endif /* REGEXP_RECURSION_LIMIT */
11431253
return ECMA_VALUE_FALSE; /* fail */
11441254
}
11451255
}
@@ -1232,6 +1342,9 @@ ecma_regexp_exec_helper (ecma_value_t regexp_value, /**< RegExp object */
12321342
re_ctx.input_start_p = input_curr_p;
12331343
const lit_utf8_byte_t *input_end_p = re_ctx.input_start_p + input_buffer_size;
12341344
re_ctx.input_end_p = input_end_p;
1345+
#ifdef REGEXP_RECURSION_LIMIT
1346+
re_ctx.recursion_depth = 0;
1347+
#endif /* REGEXP_RECURSION_LIMIT */
12351348

12361349
/* 1. Read bytecode header and init regexp matcher context. */
12371350
re_ctx.flags = bc_p->header.status_flags;

jerry-core/ecma/operations/ecma-regexp-object.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ typedef struct
4646
const lit_utf8_byte_t **saved_p; /**< saved result string pointers, ECMA 262 v5, 15.10.2.1, State */
4747
const lit_utf8_byte_t *input_start_p; /**< start of input pattern string */
4848
const lit_utf8_byte_t *input_end_p; /**< end of input pattern string */
49+
#ifdef REGEXP_RECURSION_LIMIT
50+
uint32_t recursion_depth; /**< recursion depth limit */
51+
#endif /* REGEXP_RECURSION_LIMIT */
4952
uint32_t num_of_captures; /**< number of capture groups */
5053
uint32_t num_of_non_captures; /**< number of non-capture groups */
5154
uint32_t *num_of_iterations_p; /**< number of iterations */

0 commit comments

Comments
 (0)