Skip to content

test(DataHandler): cover deeply nested reply causing decoder stack overflow - #2138

Merged
PavelPashov merged 1 commit into
redis:mainfrom
GiHoon1123:fix-2108-parser-recursion-crash
Aug 6, 2026
Merged

PavelPashov merged 1 commit into
redis:mainfrom
GiHoon1123:fix-2108-parser-recursion-crash

Conversation

@GiHoon1123

@GiHoon1123 GiHoon1123 commented Jul 14, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #2108.

redis-parser (and its replacement, the RESP3 Decoder from #2127) recurses
once per level of RESP aggregate nesting with no depth limit, so a
sufficiently deeply nested reply throws a RangeError instead of reporting a
parser error.

#2127 already wraps the decoder call in try/catch and routes failures
through the existing returnFatalError -> recoverFromFatalError path, so
this no longer needs a code change. There was no test pinning that behavior
down for this specific case, so this adds one.

Verified by temporarily removing the try/catch and confirming the new test
fails with the expected RangeError.


Note

Low Risk
The change is narrowly scoped defensive error handling around inbound parsing; the diff here is test-only, with production behavior already aligned to the existing fatal-recovery path.

Overview
This PR’s visible diff adds a regression test in DataHandler for issue #2108: feeding a reply nested ~50,000 *1 arrays must not throw synchronously and must invoke recoverFromFatalError once with a RangeError (“Maximum call stack size exceeded”) and { offlineQueue: false }.

The accompanying fix (described in the PR, not in the snippet above) wraps stream decoder.write in try/catch and forwards unexpected errors through the existing returnFatalError → recoverFromFatalError path so parser stack overflows recover instead of killing the Node process.

Reviewed by Cursor Bugbot for commit 831b5d8. Bugbot is set up for automated code reviews on this repo. Configure here.

@PavelPashov

Copy link
Copy Markdown
Contributor

@GiHoon1123 thank you for this PR, I'll take a look, however please check #2127 as it might make sense to adapt the change for that PR.

@GiHoon1123

Copy link
Copy Markdown
Contributor Author

Thanks for the pointer! I took a look at #2127 — its DataHandler.ts already wraps decoder.write() in a try/catch that routes into returnFatalError the same way, so this exact crash looks like it's already handled there for the new RESP3 decoder path.

Since #2127 is still in progress, this PR still seemed worth having as a fix for the currently released behavior - main still calls redis-parser directly with no such guard. Happy to close this in favor of #2127 once it lands, or adjust the scope here if you'd prefer a different approach - whichever is more useful, just let me know.

…er stack overflow

The RESP3 rewrite (redis#2127) replaced redis-parser with a new Decoder, and as
part of that already wraps decoder.write() in try/catch, routing failures
through returnFatalError -> recoverFromFatalError the same way other
parser-detected fatal errors are handled.

The new Decoder still recurses once per level of RESP aggregate nesting
with no depth limit (confirmed directly against lib/resp/decoder.ts), so a
sufficiently deeply nested reply still throws a RangeError - it's just
already caught and routed through the existing recovery path instead of
crashing the process. There was no test pinning this down, so this adds
one.

Fixes redis#2108
@GiHoon1123
GiHoon1123 force-pushed the fix-2108-parser-recursion-crash branch from ac38e15 to 831b5d8 Compare July 28, 2026 05:57
@GiHoon1123 GiHoon1123 changed the title fix: recover from parser errors instead of crashing the process test(DataHandler): cover deeply nested reply causing decoder stack overflow Jul 28, 2026

@PavelPashov PavelPashov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, looks good

@PavelPashov
PavelPashov merged commit dfd5428 into redis:main Aug 6, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deeply nested RESP aggregates can trigger uncaught RangeError and crash the process

2 participants