Skip to content

Commit da1149a

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 da1149a

File tree

7 files changed

+146
-1
lines changed

7 files changed

+146
-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})
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)
248+
set(DEFINES_JERRY ${DEFINES_JERRY} REGEXP_RECURSION_LIMIT=${REGEXP_RECURSION_LIMIT})
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: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,13 @@ 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+
return ecma_raise_range_error ("RegExp executor recursion limit is exceeded.");
371+
}
372+
++re_ctx_p->recursion_depth;
373+
#endif /* REGEXP_RECURSION_LIMIT */
367374
const lit_utf8_byte_t *str_curr_p = str_p;
368375

369376
while (true)
@@ -376,12 +383,18 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
376383
{
377384
JERRY_TRACE_MSG ("Execute RE_OP_MATCH: match\n");
378385
*out_str_p = str_curr_p;
386+
#ifdef REGEXP_RECURSION_LIMIT
387+
--re_ctx_p->recursion_depth;
388+
#endif /* REGEXP_RECURSION_LIMIT */
379389
return ECMA_VALUE_TRUE; /* match */
380390
}
381391
case RE_OP_CHAR:
382392
{
383393
if (str_curr_p >= re_ctx_p->input_end_p)
384394
{
395+
#ifdef REGEXP_RECURSION_LIMIT
396+
--re_ctx_p->recursion_depth;
397+
#endif /* REGEXP_RECURSION_LIMIT */
385398
return ECMA_VALUE_FALSE; /* fail */
386399
}
387400

@@ -393,6 +406,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
393406
if (ch1 != ch2)
394407
{
395408
JERRY_TRACE_MSG ("fail\n");
409+
#ifdef REGEXP_RECURSION_LIMIT
410+
--re_ctx_p->recursion_depth;
411+
#endif /* REGEXP_RECURSION_LIMIT */
396412
return ECMA_VALUE_FALSE; /* fail */
397413
}
398414

@@ -404,6 +420,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
404420
{
405421
if (str_curr_p >= re_ctx_p->input_end_p)
406422
{
423+
#ifdef REGEXP_RECURSION_LIMIT
424+
--re_ctx_p->recursion_depth;
425+
#endif /* REGEXP_RECURSION_LIMIT */
407426
return ECMA_VALUE_FALSE; /* fail */
408427
}
409428

@@ -413,6 +432,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
413432
if (lit_char_is_line_terminator (ch))
414433
{
415434
JERRY_TRACE_MSG ("fail\n");
435+
#ifdef REGEXP_RECURSION_LIMIT
436+
--re_ctx_p->recursion_depth;
437+
#endif /* REGEXP_RECURSION_LIMIT */
416438
return ECMA_VALUE_FALSE; /* fail */
417439
}
418440

@@ -432,6 +454,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
432454
if (!(re_ctx_p->flags & RE_FLAG_MULTILINE))
433455
{
434456
JERRY_TRACE_MSG ("fail\n");
457+
#ifdef REGEXP_RECURSION_LIMIT
458+
--re_ctx_p->recursion_depth;
459+
#endif /* REGEXP_RECURSION_LIMIT */
435460
return ECMA_VALUE_FALSE; /* fail */
436461
}
437462

@@ -442,6 +467,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
442467
}
443468

444469
JERRY_TRACE_MSG ("fail\n");
470+
#ifdef REGEXP_RECURSION_LIMIT
471+
--re_ctx_p->recursion_depth;
472+
#endif /* REGEXP_RECURSION_LIMIT */
445473
return ECMA_VALUE_FALSE; /* fail */
446474
}
447475
case RE_OP_ASSERT_END:
@@ -457,6 +485,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
457485
if (!(re_ctx_p->flags & RE_FLAG_MULTILINE))
458486
{
459487
JERRY_TRACE_MSG ("fail\n");
488+
#ifdef REGEXP_RECURSION_LIMIT
489+
--re_ctx_p->recursion_depth;
490+
#endif /* REGEXP_RECURSION_LIMIT */
460491
return ECMA_VALUE_FALSE; /* fail */
461492
}
462493

@@ -467,6 +498,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
467498
}
468499

469500
JERRY_TRACE_MSG ("fail\n");
501+
#ifdef REGEXP_RECURSION_LIMIT
502+
--re_ctx_p->recursion_depth;
503+
#endif /* REGEXP_RECURSION_LIMIT */
470504
return ECMA_VALUE_FALSE; /* fail */
471505
}
472506
case RE_OP_ASSERT_WORD_BOUNDARY:
@@ -498,6 +532,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
498532
if (is_wordchar_left == is_wordchar_right)
499533
{
500534
JERRY_TRACE_MSG ("fail\n");
535+
#ifdef REGEXP_RECURSION_LIMIT
536+
--re_ctx_p->recursion_depth;
537+
#endif /* REGEXP_RECURSION_LIMIT */
501538
return ECMA_VALUE_FALSE; /* fail */
502539
}
503540
}
@@ -509,6 +546,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
509546
if (is_wordchar_left != is_wordchar_right)
510547
{
511548
JERRY_TRACE_MSG ("fail\n");
549+
#ifdef REGEXP_RECURSION_LIMIT
550+
--re_ctx_p->recursion_depth;
551+
#endif /* REGEXP_RECURSION_LIMIT */
512552
return ECMA_VALUE_FALSE; /* fail */
513553
}
514554
}
@@ -563,6 +603,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
563603

564604
if (!ECMA_IS_VALUE_ERROR (match_value))
565605
{
606+
#ifdef REGEXP_RECURSION_LIMIT
607+
--re_ctx_p->recursion_depth;
608+
#endif /* REGEXP_RECURSION_LIMIT */
566609
if (ecma_is_value_true (match_value))
567610
{
568611
*out_str_p = sub_str_p;
@@ -588,6 +631,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
588631
if (str_curr_p >= re_ctx_p->input_end_p)
589632
{
590633
JERRY_TRACE_MSG ("fail\n");
634+
#ifdef REGEXP_RECURSION_LIMIT
635+
--re_ctx_p->recursion_depth;
636+
#endif /* REGEXP_RECURSION_LIMIT */
591637
return ECMA_VALUE_FALSE; /* fail */
592638
}
593639

@@ -618,6 +664,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
618664
if (!is_match)
619665
{
620666
JERRY_TRACE_MSG ("fail\n");
667+
#ifdef REGEXP_RECURSION_LIMIT
668+
--re_ctx_p->recursion_depth;
669+
#endif /* REGEXP_RECURSION_LIMIT */
621670
return ECMA_VALUE_FALSE; /* fail */
622671
}
623672
}
@@ -627,6 +676,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
627676
if (is_match)
628677
{
629678
JERRY_TRACE_MSG ("fail\n");
679+
#ifdef REGEXP_RECURSION_LIMIT
680+
--re_ctx_p->recursion_depth;
681+
#endif /* REGEXP_RECURSION_LIMIT */
630682
return ECMA_VALUE_FALSE; /* fail */
631683
}
632684
}
@@ -657,6 +709,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
657709
if (str_curr_p >= re_ctx_p->input_end_p)
658710
{
659711
JERRY_TRACE_MSG ("fail\n");
712+
#ifdef REGEXP_RECURSION_LIMIT
713+
--re_ctx_p->recursion_depth;
714+
#endif /* REGEXP_RECURSION_LIMIT */
660715
return ECMA_VALUE_FALSE; /* fail */
661716
}
662717

@@ -666,6 +721,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
666721
if (ch1 != ch2)
667722
{
668723
JERRY_TRACE_MSG ("fail\n");
724+
#ifdef REGEXP_RECURSION_LIMIT
725+
--re_ctx_p->recursion_depth;
726+
#endif /* REGEXP_RECURSION_LIMIT */
669727
return ECMA_VALUE_FALSE; /* fail */
670728
}
671729
}
@@ -689,6 +747,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
689747
if (ecma_is_value_true (match_value))
690748
{
691749
*out_str_p = sub_str_p;
750+
#ifdef REGEXP_RECURSION_LIMIT
751+
--re_ctx_p->recursion_depth;
752+
#endif /* REGEXP_RECURSION_LIMIT */
692753
return match_value; /* match */
693754
}
694755
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -703,13 +764,19 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
703764
bc_p = old_bc_p;
704765

705766
re_ctx_p->saved_p[RE_GLOBAL_START_IDX] = old_start_p;
767+
#ifdef REGEXP_RECURSION_LIMIT
768+
--re_ctx_p->recursion_depth;
769+
#endif /* REGEXP_RECURSION_LIMIT */
706770
return ECMA_VALUE_FALSE; /* fail */
707771
}
708772
case RE_OP_SAVE_AND_MATCH:
709773
{
710774
JERRY_TRACE_MSG ("End of pattern is reached: match\n");
711775
re_ctx_p->saved_p[RE_GLOBAL_END_IDX] = str_curr_p;
712776
*out_str_p = str_curr_p;
777+
#ifdef REGEXP_RECURSION_LIMIT
778+
--re_ctx_p->recursion_depth;
779+
#endif /* REGEXP_RECURSION_LIMIT */
713780
return ECMA_VALUE_TRUE; /* match */
714781
}
715782
case RE_OP_ALTERNATIVE:
@@ -774,6 +841,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
774841
if (ecma_is_value_true (match_value))
775842
{
776843
*out_str_p = sub_str_p;
844+
#ifdef REGEXP_RECURSION_LIMIT
845+
--re_ctx_p->recursion_depth;
846+
#endif /* REGEXP_RECURSION_LIMIT */
777847
return match_value; /* match */
778848
}
779849
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -832,6 +902,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
832902
if (ecma_is_value_true (match_value))
833903
{
834904
*out_str_p = sub_str_p;
905+
#ifdef REGEXP_RECURSION_LIMIT
906+
--re_ctx_p->recursion_depth;
907+
#endif /* REGEXP_RECURSION_LIMIT */
835908
return match_value; /* match */
836909
}
837910
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -856,6 +929,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
856929
if (ecma_is_value_true (match_value))
857930
{
858931
*out_str_p = sub_str_p;
932+
#ifdef REGEXP_RECURSION_LIMIT
933+
--re_ctx_p->recursion_depth;
934+
#endif /* REGEXP_RECURSION_LIMIT */
859935
return match_value; /* match */
860936
}
861937
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -865,6 +941,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
865941
}
866942

867943
re_ctx_p->saved_p[start_idx] = old_start_p;
944+
#ifdef REGEXP_RECURSION_LIMIT
945+
--re_ctx_p->recursion_depth;
946+
#endif /* REGEXP_RECURSION_LIMIT */
868947
return ECMA_VALUE_FALSE; /* fail */
869948
}
870949
case RE_OP_CAPTURE_NON_GREEDY_GROUP_END:
@@ -910,6 +989,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
910989
if (ecma_is_value_true (match_value))
911990
{
912991
*out_str_p = sub_str_p;
992+
#ifdef REGEXP_RECURSION_LIMIT
993+
--re_ctx_p->recursion_depth;
994+
#endif /* REGEXP_RECURSION_LIMIT */
913995
return match_value; /* match */
914996
}
915997
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -958,6 +1040,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
9581040
if (re_ctx_p->num_of_iterations_p[iter_idx] >= min
9591041
&& str_curr_p== re_ctx_p->saved_p[start_idx])
9601042
{
1043+
#ifdef REGEXP_RECURSION_LIMIT
1044+
--re_ctx_p->recursion_depth;
1045+
#endif /* REGEXP_RECURSION_LIMIT */
9611046
return ECMA_VALUE_FALSE; /* fail */
9621047
}
9631048

@@ -979,6 +1064,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
9791064
if (ecma_is_value_true (match_value))
9801065
{
9811066
*out_str_p = sub_str_p;
1067+
#ifdef REGEXP_RECURSION_LIMIT
1068+
--re_ctx_p->recursion_depth;
1069+
#endif /* REGEXP_RECURSION_LIMIT */
9821070
return match_value; /* match */
9831071
}
9841072
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -1003,6 +1091,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
10031091
if (ecma_is_value_true (match_value))
10041092
{
10051093
*out_str_p = sub_str_p;
1094+
#ifdef REGEXP_RECURSION_LIMIT
1095+
--re_ctx_p->recursion_depth;
1096+
#endif /* REGEXP_RECURSION_LIMIT */
10061097
return match_value; /* match */
10071098
}
10081099
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -1024,6 +1115,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
10241115
if (ecma_is_value_true (match_value))
10251116
{
10261117
*out_str_p = sub_str_p;
1118+
#ifdef REGEXP_RECURSION_LIMIT
1119+
--re_ctx_p->recursion_depth;
1120+
#endif /* REGEXP_RECURSION_LIMIT */
10271121
return match_value; /* match */
10281122
}
10291123
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -1035,6 +1129,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
10351129
/* restore if fails */
10361130
re_ctx_p->saved_p[end_idx] = old_end_p;
10371131
re_ctx_p->num_of_iterations_p[iter_idx]--;
1132+
#ifdef REGEXP_RECURSION_LIMIT
1133+
--re_ctx_p->recursion_depth;
1134+
#endif /* REGEXP_RECURSION_LIMIT */
10381135
return ECMA_VALUE_FALSE; /* fail */
10391136
}
10401137
case RE_OP_NON_GREEDY_ITERATOR:
@@ -1059,6 +1156,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
10591156
if (ecma_is_value_true (match_value))
10601157
{
10611158
*out_str_p = sub_str_p;
1159+
#ifdef REGEXP_RECURSION_LIMIT
1160+
--re_ctx_p->recursion_depth;
1161+
#endif /* REGEXP_RECURSION_LIMIT */
10621162
return match_value; /* match */
10631163
}
10641164
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -1082,6 +1182,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
10821182
str_curr_p = sub_str_p;
10831183
num_of_iter++;
10841184
}
1185+
#ifdef REGEXP_RECURSION_LIMIT
1186+
--re_ctx_p->recursion_depth;
1187+
#endif /* REGEXP_RECURSION_LIMIT */
10851188
return ECMA_VALUE_FALSE; /* fail */
10861189
}
10871190
default:
@@ -1125,6 +1228,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
11251228
if (ecma_is_value_true (match_value))
11261229
{
11271230
*out_str_p = sub_str_p;
1231+
#ifdef REGEXP_RECURSION_LIMIT
1232+
--re_ctx_p->recursion_depth;
1233+
#endif /* REGEXP_RECURSION_LIMIT */
11281234
return match_value; /* match */
11291235
}
11301236
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -1140,6 +1246,9 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
11401246
lit_utf8_read_prev (&str_curr_p);
11411247
num_of_iter--;
11421248
}
1249+
#ifdef REGEXP_RECURSION_LIMIT
1250+
--re_ctx_p->recursion_depth;
1251+
#endif /* REGEXP_RECURSION_LIMIT */
11431252
return ECMA_VALUE_FALSE; /* fail */
11441253
}
11451254
}
@@ -1232,6 +1341,9 @@ ecma_regexp_exec_helper (ecma_value_t regexp_value, /**< RegExp object */
12321341
re_ctx.input_start_p = input_curr_p;
12331342
const lit_utf8_byte_t *input_end_p = re_ctx.input_start_p + input_buffer_size;
12341343
re_ctx.input_end_p = input_end_p;
1344+
#ifdef REGEXP_RECURSION_LIMIT
1345+
re_ctx.recursion_depth = 0;
1346+
#endif /* REGEXP_RECURSION_LIMIT */
12351347

12361348
/* 1. Read bytecode header and init regexp matcher context. */
12371349
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)