Reported from zig-js, where regular expression literals reach this library directly from JavaScript source: zig-utils/zig-js#934.
Valid patterns that segfault
Each row was executed through zig-js's current test262 --eval binary, with the recursion taken from an lldb backtrace at the crash. node compiles and runs each pattern.
| pattern | size | zig-regex | node | recursion |
|---|---|---|---|---|
/aaaa…a/, a flat concatenation | 30,000 chars | SIGSEGV | runs | Compiler.compileNode (compiler.zig:217), about 26,850 frames |
/a|a|…|a/, a flat alternation | 15,000 alternatives | SIGSEGV | runs | compileNode <-> compileAlternation (compiler.zig:321), about 13,411 each |
/(?=a)aaaa…a/ matched against 31,000 as | 31,000 | SIGSEGV at match time | runs | BacktrackEngine.matchNode (backtrack.zig:182) <-> matchConcat (336) |
| the same pattern, length 14,000 | 14,000 | OK | runs | — |
/aaaa…a/, 2,000 chars | 2,000 | OK | runs | — |
The first two are flat, not nested, so they are ordinary large patterns rather than adversarial ones.
Other recursive walkers found
A mapping pass over the library found these recursing over pattern structure with no guard. Only the three rows above are confirmed crashes; the rest are listed so they are not lost:
Parser.parseSetExpr (parser.zig:2130), Parser.validateDuplicateGroupNames (parser.zig:636), PatternAnalyzer.analyzeNode (pattern_analyzer.zig:10), optimizer.scanFeatures (optimizer.zig:335), requiresBacktracking (regex.zig:2611), onepass Builder.flatten (onepass.zig:176), ClassSet.matchesMode (ast.zig:164).
Two different fixes
- Flat concatenation and alternation are sequences. Compiling them iteratively, instead of recursing once per element, lets these patterns work — node accepts them, so failing with an error would still be incompatible.
- Genuine nesting (deep groups, nested classes) and backtracking depth need a bounded depth or an explicit backtrack stack, and a clean error return the host engine can map to a JavaScript
SyntaxErrororRangeError, rather than a process crash.