Skip to content

Commit 4a9e185

Browse files
authored
Rework the engine's internal recursion limit (#2969)
This patch unifies the recursion limit checking for RegExp, function call and JSON as well. Until now the limit was only a counter which was increased/decreased at certain points. This counter has been substituted with a numeric limit which allows to restrict the stack usage. This patch fixes #2963 and resolves the closed #2258 issue. Co-authored-by: Gabor Loki loki@inf.u-szeged.hu JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu
1 parent f53dba1 commit 4a9e185

20 files changed

+156
-216
lines changed

.travis.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@ matrix:
7373

7474
- name: "ASAN Tests"
7575
env:
76-
- OPTS="--quiet --jerry-tests --jerry-test-suite --skip-list=parser-oom.js,parser-oom2.js --buildoptions=--compile-flag=-fsanitize=address,--compile-flag=-m32,--compile-flag=-fno-omit-frame-pointer,--compile-flag=-fno-common,--compile-flag=-O2,--debug,--system-allocator=on,--linker-flag=-fuse-ld=gold"
76+
# Skipping maximum stack usage related tests due to 'detect_stack_use_after_return=1' ASAN option.
77+
# For more detailed description: https://github.com/google/sanitizers/wiki/AddressSanitizerUseAfterReturn#compatibility
78+
- OPTS="--quiet --jerry-tests --jerry-test-suite --skip-list=parser-oom.js,parser-oom2.js,stack-limit.js,regression-test-issue-2190.js,regression-test-issue-2258-2963.js,regression-test-issue-2448.js,regression-test-issue-2905.js --buildoptions=--stack-limit=0,--compile-flag=-fsanitize=address,--compile-flag=-m32,--compile-flag=-fno-omit-frame-pointer,--compile-flag=-fno-common,--compile-flag=-O2,--debug,--system-allocator=on,--linker-flag=-fuse-ld=gold"
7779
- ASAN_OPTIONS=detect_stack_use_after_return=1:check_initialization_order=true:strict_init_order=true
7880
- TIMEOUT=600
7981
compiler: gcc-5

jerry-core/CMakeLists.txt

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ set(JERRY_SYSTEM_ALLOCATOR OFF CACHE BOOL "Enable system allocato
3939
set(JERRY_VALGRIND OFF CACHE BOOL "Enable Valgrind support?")
4040
set(JERRY_VM_EXEC_STOP OFF CACHE BOOL "Enable VM execution stopping?")
4141
set(JERRY_GLOBAL_HEAP_SIZE "(512)" CACHE STRING "Size of memory heap, in kilobytes")
42-
set(JERRY_REGEXP_RECURSION_LIMIT "0" CACHE STRING "Limit of regexp recursion depth")
43-
set(JERRY_CALL_STACK_LIMIT "0" CACHE STRING "Limit of function call recursion depth")
42+
set(JERRY_STACK_LIMIT "(0)" CACHE STRING "Maximum stack usage size, in kilobytes")
4443

4544
# Option overrides
4645
if(USING_MSVC)
@@ -102,8 +101,7 @@ message(STATUS "JERRY_SYSTEM_ALLOCATOR " ${JERRY_SYSTEM_ALLOCATOR})
102101
message(STATUS "JERRY_VALGRIND " ${JERRY_VALGRIND})
103102
message(STATUS "JERRY_VM_EXEC_STOP " ${JERRY_VM_EXEC_STOP})
104103
message(STATUS "JERRY_GLOBAL_HEAP_SIZE " ${JERRY_GLOBAL_HEAP_SIZE})
105-
message(STATUS "JERRY_REGEXP_RECURSION_LIMIT " ${JERRY_REGEXP_RECURSION_LIMIT})
106-
message(STATUS "JERRY_CALL_STACK_LIMIT " ${JERRY_CALL_STACK_LIMIT})
104+
message(STATUS "JERRY_STACK_LIMIT " ${JERRY_STACK_LIMIT})
107105

108106
# Include directories
109107
set(INCLUDE_CORE_PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
@@ -275,16 +273,6 @@ endif()
275273
# RegExp strict mode
276274
jerry_add_define01(JERRY_REGEXP_STRICT_MODE)
277275

278-
# RegExp recursion depth limit
279-
if(JERRY_REGEXP_RECURSION_LIMIT)
280-
set(DEFINES_JERRY ${DEFINES_JERRY} JERRY_REGEXP_RECURSION_LIMIT=${JERRY_REGEXP_RECURSION_LIMIT})
281-
endif()
282-
283-
# Function call recursion depth limit
284-
if(JERRY_CALL_STACK_LIMIT)
285-
set(DEFINES_JERRY ${DEFINES_JERRY} JERRY_CALL_STACK_LIMIT=${JERRY_CALL_STACK_LIMIT})
286-
endif()
287-
288276
# RegExp byte-code dumps
289277
jerry_add_define01(JERRY_REGEXP_DUMP_BYTE_CODE)
290278

@@ -309,6 +297,9 @@ jerry_add_define01(JERRY_VM_EXEC_STOP)
309297
# Size of heap
310298
set(DEFINES_JERRY ${DEFINES_JERRY} JERRY_GLOBAL_HEAP_SIZE=${JERRY_GLOBAL_HEAP_SIZE})
311299

300+
# Maximum size of stack memory usage
301+
set(DEFINES_JERRY ${DEFINES_JERRY} JERRY_STACK_LIMIT=${JERRY_STACK_LIMIT})
302+
312303
add_library(${JERRY_CORE_NAME} ${SOURCE_CORE_FILES})
313304

314305
target_compile_definitions(${JERRY_CORE_NAME} PUBLIC ${DEFINES_JERRY})

jerry-core/config.h

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,19 @@
205205
# define JERRY_GLOBAL_HEAP_SIZE (512)
206206
#endif /* !defined (JERRY_GLOBAL_HEAP_SIZE) */
207207

208+
/**
209+
* Maximum stack usage size in kilobytes
210+
*
211+
* Note: This feature cannot be used when 'detect_stack_use_after_return=1' ASAN option is enabled.
212+
* For more detailed description:
213+
* - https://github.com/google/sanitizers/wiki/AddressSanitizerUseAfterReturn#compatibility
214+
*
215+
* Default value: 0, unlimited
216+
*/
217+
#ifndef JERRY_STACK_LIMIT
218+
# define JERRY_STACK_LIMIT (0)
219+
#endif /* !defined (JERRY_STACK_LIMIT) */
220+
208221
/**
209222
* Enable/Disable property lookup cache.
210223
*
@@ -352,22 +365,6 @@
352365
# define JERRY_REGEXP_STRICT_MODE 0
353366
#endif /* !defined (JERRY_REGEXP_STRICT_MODE) */
354367

355-
/**
356-
* Set the RegExp parser and execution recursion limit.
357-
*
358-
* Allowed values:
359-
* 0: Disable recursion limit check.
360-
* 1 or greater: Set the recursion limit to the given number.
361-
*
362-
* Note:
363-
* A negative value will cause a static assert compiler error.
364-
*
365-
* Default value: 0
366-
*/
367-
#ifndef JERRY_REGEXP_RECURSION_LIMIT
368-
# define JERRY_REGEXP_RECURSION_LIMIT 0
369-
#endif /* !defined (JERRY_REGEXP_RECURSION_LIMIT) */
370-
371368
/**
372369
* Enable/Disable the snapshot execution functions.
373370
*
@@ -435,23 +432,6 @@
435432
# define JERRY_VM_EXEC_STOP 0
436433
#endif /* !defined (JERRY_VM_EXEC_STOP) */
437434

438-
/**
439-
* Set the function call recursion limit.
440-
*
441-
* Allowed values:
442-
* 0: Disable recursion limit check.
443-
* 1 or greater: Set the recursion limit to the given number.
444-
*
445-
* Note:
446-
* A negative value will cause a static assert compiler error.
447-
*
448-
* Default value: 0
449-
*/
450-
#ifndef JERRY_CALL_STACK_LIMIT
451-
# define JERRY_CALL_STACK_LIMIT 0
452-
#endif /* !defined (JERRY_CALL_STACK_LIMIT) */
453-
454-
455435
/**
456436
* Advanced section configurations.
457437
*/
@@ -631,6 +611,9 @@
631611
#if !defined (JERRY_GLOBAL_HEAP_SIZE) || (JERRY_GLOBAL_HEAP_SIZE <= 0)
632612
# error "Invalid value for 'JERRY_GLOBAL_HEAP_SIZE' macro."
633613
#endif
614+
#if !defined (JERRY_STACK_LIMIT) || (JERRY_STACK_LIMIT < 0)
615+
# error "Invalid value for 'JERRY_STACK_LIMIT' macro."
616+
#endif
634617
#if !defined (JERRY_LCACHE) \
635618
|| ((JERRY_LCACHE != 0) && (JERRY_LCACHE != 1))
636619
# error "Invalid value for 'JERRY_LCACHE' macro."

jerry-core/ecma/base/ecma-globals.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,6 +1542,25 @@ typedef struct
15421542
*/
15431543
#define ECMA_SYMBOL_HASH_SHIFT 2
15441544

1545+
#if (JERRY_STACK_LIMIT != 0)
1546+
/**
1547+
* Check the current stack usage. If the limit is reached a RangeError is raised.
1548+
*/
1549+
#define ECMA_CHECK_STACK_USAGE() \
1550+
do \
1551+
{ \
1552+
if (ecma_get_current_stack_usage () > CONFIG_MEM_STACK_LIMIT) \
1553+
{ \
1554+
return ecma_raise_range_error (ECMA_ERR_MSG ("Maximum call stack size exceeded.")); \
1555+
} \
1556+
} while (0)
1557+
#else /* JERRY_STACK_LIMIT == 0) */
1558+
/**
1559+
* If the stack limit is unlimited, this check is an empty macro.
1560+
*/
1561+
#define ECMA_CHECK_STACK_USAGE()
1562+
#endif /* (JERRY_STACK_LIMIT != 0) */
1563+
15451564
/**
15461565
* @}
15471566
* @}

jerry-core/ecma/base/ecma-helpers.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1599,6 +1599,21 @@ ecma_bytecode_deref (ecma_compiled_code_t *bytecode_p) /**< byte code pointer */
15991599
((size_t) bytecode_p->size) << JMEM_ALIGNMENT_LOG);
16001600
} /* ecma_bytecode_deref */
16011601

1602+
#if (JERRY_STACK_LIMIT != 0)
1603+
/**
1604+
* Check the current stack usage by calculating the difference from the initial stack base.
1605+
*
1606+
* @return current stack usage in bytes
1607+
*/
1608+
uintptr_t JERRY_ATTR_NOINLINE
1609+
ecma_get_current_stack_usage (void)
1610+
{
1611+
volatile int __sp;
1612+
return (uintptr_t) (JERRY_CONTEXT (stack_base) - (uintptr_t)&__sp);
1613+
} /* ecma_get_current_stack_usage */
1614+
1615+
#endif /* (JERRY_STACK_LIMIT != 0) */
1616+
16021617
/**
16031618
* @}
16041619
* @}

jerry-core/ecma/base/ecma-helpers.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,9 @@ ecma_value_t ecma_clear_error_reference (ecma_value_t value, bool set_abort_flag
393393

394394
void ecma_bytecode_ref (ecma_compiled_code_t *bytecode_p);
395395
void ecma_bytecode_deref (ecma_compiled_code_t *bytecode_p);
396+
#if (JERRY_STACK_LIMIT != 0)
397+
uintptr_t ecma_get_current_stack_usage (void);
398+
#endif /* (JERRY_STACK_LIMIT != 0) */
396399

397400
/* ecma-helpers-external-pointers.c */
398401
bool ecma_create_native_pointer_property (ecma_object_t *obj_p, void *native_p, void *info_p);

jerry-core/ecma/base/ecma-init-finalize.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,10 @@ ecma_init (void)
4242
JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_HIGH_PRESSURE_GC;
4343
#endif /* ENABLED (JERRY_PROPRETY_HASHMAP) */
4444

45-
#if defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0)
46-
JERRY_CONTEXT (function_call_counter) = JERRY_CALL_STACK_LIMIT;
47-
#endif /* defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0) */
45+
#if (JERRY_STACK_LIMIT != 0)
46+
volatile int sp;
47+
JERRY_CONTEXT (stack_base) = (uintptr_t)&sp;
48+
#endif /* (JERRY_STACK_LIMIT != 0) */
4849

4950
#if ENABLED (JERRY_ES2015_BUILTIN_PROMISE)
5051
ecma_job_queue_init ();

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

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -705,16 +705,7 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */
705705
|| ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_BOUND_FUNCTION
706706
|| !ecma_op_function_has_construct_flag (arguments_list_p));
707707

708-
#if defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0)
709-
if (JERRY_UNLIKELY (JERRY_CONTEXT (function_call_counter) == 0))
710-
{
711-
return ecma_raise_range_error (ECMA_ERR_MSG ("Maximum call stack size is exceeded."));
712-
}
713-
else
714-
{
715-
JERRY_CONTEXT (function_call_counter)--;
716-
}
717-
#endif /* defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0) */
708+
ECMA_CHECK_STACK_USAGE ();
718709

719710
switch (ecma_get_object_type (func_obj_p))
720711
{
@@ -729,10 +720,6 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */
729720
arguments_list_p,
730721
arguments_list_len);
731722

732-
#if defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0)
733-
JERRY_CONTEXT (function_call_counter)++;
734-
#endif /* defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0) */
735-
736723
return ret_value;
737724
}
738725

@@ -823,10 +810,6 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */
823810
ecma_free_value (this_binding);
824811
}
825812

826-
#if defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0)
827-
JERRY_CONTEXT (function_call_counter)++;
828-
#endif /* defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0) */
829-
830813
return ret_value;
831814
}
832815
case ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION:
@@ -837,9 +820,6 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */
837820
this_arg_value,
838821
arguments_list_p,
839822
arguments_list_len);
840-
#if defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0)
841-
JERRY_CONTEXT (function_call_counter)++;
842-
#endif /* defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0) */
843823

844824
if (JERRY_UNLIKELY (ecma_is_value_error_reference (ret_value)))
845825
{
@@ -888,10 +868,6 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */
888868
ecma_deref_object (local_env_p);
889869
}
890870

891-
#if defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0)
892-
JERRY_CONTEXT (function_call_counter)++;
893-
#endif /* defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0) */
894-
895871
return ret_value;
896872
}
897873
#endif /* ENABLED (JERRY_ES2015_ARROW_FUNCTION) */
@@ -902,10 +878,6 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */
902878
}
903879
}
904880

905-
#if defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0)
906-
JERRY_CONTEXT (function_call_counter)++;
907-
#endif /* defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0) */
908-
909881
JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_DIRECT_EVAL;
910882

911883
ecma_extended_object_t *ext_function_p;

0 commit comments

Comments
 (0)