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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions