Skip to content

Infinite recurssion in regex handling #5286

@davidlie

Description

@davidlie

Applying a large quantifier to a zero-width lookahead causes ecma_regexp_run to recurse once per quantifier iteration. Because the lookahead consumes no input, the engine never makes forward progress and recurses ~10,000 times before overflowing the native stack. There is no recursion-depth counter in the regex engine's backtracking loop.

Trigger (minimal):

/(?:(?=x)){10000}xyz/.exec('xyz');

Crash type: stack-overflow (SIGABRT / rc=1)
Component: jerry-core/ecma/operations/ecma-regexp-object.c, ecma_regexp_run

Note this is probably the same issue as #5265

JerryScript revision

git hash b706935 on master branch

Build platform

Ubuntu 24.04.4 LTS (Linux 6.8.0-106-generic x86_64)

Build steps
tools/build.py --builddir build-asan --build-type Debug \
  --compile-flag=-fsanitize=address \
  --compile-flag=-fno-omit-frame-pointer \
  --compile-flag=-g \
  --linker-flag=-fsanitize=address
Test case
echo "/(?:(?=x)){10000}xyz/.exec('xyz');" \
  | ASAN_OPTIONS=halt_on_error=0 build-asan/bin/jerry -
Output
==ERROR: AddressSanitizer: stack-overflow on address 0x7fff5f3baf18
         (pc 0x...05393fd2 bp 0x7fff5f3bb2c0 sp 0x7fff5f3baef0 T0)
Backtrace
    #0  ecma_regexp_run   ecma-regexp-object.c:535   ← stack limit hit here
    #1  ecma_regexp_run   ecma-regexp-object.c:778   ┐
    #2  ecma_regexp_run   ecma-regexp-object.c:1238  │ alternating pair,
    #3  ecma_regexp_run   ecma-regexp-object.c:778   │ repeats ~120×
    #4  ecma_regexp_run   ecma-regexp-object.c:1238  ┘
    ... [truncated — frames #1–#246 are the same two call sites]

SUMMARY: AddressSanitizer: stack-overflow
         ecma-regexp-object.c:535 in ecma_regexp_run

The two recursive call sites are:

  • line 778 — quantifier iteration dispatch: calls ecma_regexp_run for each repetition of the {N} quantifier.
  • line 1238 — lookahead dispatch: calls ecma_regexp_run to evaluate the (?=x) sub-expression.

Because (?=x) is zero-width, the quantifier never makes forward progress in the input string, so every one of the 10,000 iterations produces a recursive call. With ~120 frames available before native stack exhaustion, the overflow occurs roughly 83× deeper than any guard could catch.

Expected behavior

ecma_regexp_run should maintain a recursion depth counter and throw a RangeError when a configurable limit is exceeded. An alternative is to convert quantifier dispatch from recursion to an explicit loop for the common zero-width case, eliminating unbounded stack growth entirely.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions