# javascript can't stop winning

Source: https://www.youtube.com/watch?v=y2ujgpIZ5O8
Recap page: https://rapidrecap.app/video/y2ujgpIZ5O8
Generated: 2026-01-22T16:05:26.492+00:00

---
## Quick Overview

A Denial-of-Service (DoS) vulnerability in Node.js versions prior to 20.20.0, 22.22.0, 24.13.0, and 25.3.0, caused by stack overflow errors during deep recursion when using async_hooks, is mitigated by patching Node.js, as the error was being treated as a non-recoverable fatal error instead of being re-thrown to user code for graceful handling.

**Key Points:**
- The vulnerability stems from unrecoverable stack space exhaustion caused by deep recursion when async_hooks are used, leading Node.js to exit immediately with code 7 instead of allowing try-catch blocks to handle the RangeError.
- The issue specifically affects applications using async_hooks, including React Server Components, Next.js, and most Application Performance Monitoring (APM) tools that rely on async context tracking via AsyncLocalStorage or similar mechanisms.
- Node.js V8 treats async_hooks callbacks as fatal errors (kFatal) wrapped in a TryCatchScope, causing process exit upon stack overflow, which bypasses standard unhandled exception handlers.
- ECMAScript specifies that proper tail calls should reuse stack frames, but V8 does not implement this, meaning deep recursion consumes stack space, leading to the overflow.
- The fix implemented in Node.js security releases checks if the caught error is a stack overflow error and, if so, re-throws it as a standard error allowing user-level try-catch blocks to handle it gracefully, rather than exiting fatally.
- Patched releases include Node.js 20.20.0 (LTS), 22.22.0 (LTS), 24.13.0 (LTS), and 25.3.0 (Current) as of January 13, 2026.
- The vulnerability is a DoS vector, not a security vulnerability in the V8 engine itself, as V8 handles stack overflow as an unspecified behavior, not a security issue.

![Screenshot at 00:02: The title slide of the Node.js security announcement, 'Mitigating Denial-of-Service Vulnerability from Unrecoverable Stack Space Exhaustion for React, Next.js, and APM Users', clearly defines the scope of the security issue being discussed.](https://ss.rapidrecap.app/screens/y2ujgpIZ5O8/00-00-02.jpg)

**Context:** This video analyzes a critical Denial-of-Service (DoS) vulnerability discussed in a Node.js security blog post, authored by Matteo Collina and Joyee Cheung. The vulnerability specifically targets applications utilizing the `async_hooks` API, which is heavily relied upon by modern frameworks like React Server Components and Next.js, as well as various Application Performance Monitoring (APM) tools, for tracking asynchronous context across operations. The core problem arises when deep, recursive operations exhaust the call stack, causing Node.js to exit abruptly instead of recovering.

## Detailed Analysis

The video explains a Denial-of-Service vulnerability in Node.js versions preceding 20.20.0, 22.22.0, 24.13.0, and 25.3.0, which arises from unrecoverable stack space exhaustion due to deep recursion when using `async_hooks`. When a stack overflow occurs in user code while hooks are active, Node.js immediately exits with code 7, bypassing standard error handling mechanisms like `try-catch` blocks because the error is wrapped in a fatal `TryCatchScope::kFatal` handler internally. This is particularly impactful for users of Next.js and APM tools like Datadog, New Relic, etc., as they heavily leverage `AsyncLocalStorage` (built on `async_hooks`) for request context tracking. The technical deep dive reveals that V8 does not implement ECMAScript's specification for tail call reuse, meaning recursive calls continuously consume stack space until the limit is hit. The fix involves modifying the internal error handling within the `TryCatchScope` to check if the error is a stack overflow (`IsStackOverflowError`). If it is, the error is re-thrown as a standard error, allowing user code to catch the `RangeError: Maximum call stack size exceeded` gracefully instead of crashing the process fatally. The presenter confirms that Rust, unlike Node.js/V8, handles recursion more safely by reusing stack frames, making it less susceptible to this specific DoS vector.

### TL;DR

- Node.js V8 attempts to recover from stack exhaustion with a catchable error, but a bug only reproducible when async_hooks are used breaks this attempt, causing Node.js to exit with code 7 instead of throwing a catchable error.

### The Bug

- Stack overflow in user code with async_hooks enabled causes immediate process exit (code 7) by skipping process.on('uncaughtException') handlers, making the exception uncatchable by standard means.

### Call Stack Mechanics

- Recursive functions without tail call optimization (which V8 lacks) allocate new stack frames for every call, leading to stack exhaustion, where the allocated memory limit is exceeded.

### The Fatal TryCatchScope

- Node.js wraps async_hooks callbacks in a special error handler, TryCatchScope with CatchMode::kFatal, meaning any error within the hook execution results in an unrecoverable process exit immediately.

### V8 Doesn't Treat This as a Security Issue

- V8 implements stack handling differently than browsers, and while stack overflow can cause crashes, V8 does not classify this specific behavior as a security vulnerability.

### The Fix

- Inside the fatal error handler, a check is added to determine if the error is a stack overflow; if so, it re-throws the error, allowing user-level try-catch blocks to catch the RangeError gracefully instead of exiting fatally.

### Affected Versions

- Patch releases include Node.js 20.20.0 (LTS), 22.22.0 (LTS), 24.13.0 (LTS), and 25.3.0 (Current); versions from 8.x to 18.x are also affected if they use async_hooks.

![Screenshot at 00:02: The title slide of the Node.js security announcement, 'Mitigating Denial-of-Service Vulnerability from Unrecoverable Stack Space Exhaustion for React, Next.js, and APM Users', clearly defines the scope of the security issue being discussed.](https://ss.rapidrecap.app/screens/y2ujgpIZ5O8/00-00-02.jpg)
![Screenshot at 00:54: A diagram illustrating the Call Stack, noting it is a LIFO data structure where factorial\(1\) is the last added but the first to be popped off, visually explaining the concept of stack frames.](https://ss.rapidrecap.app/screens/y2ujgpIZ5O8/00-00-54.jpg)
![Screenshot at 02:21: A code snippet showing a Next.js API route handler that recursively calls processNestedData, which can lead to stack overflow if the input JSON is deeply nested.](https://ss.rapidrecap.app/screens/y2ujgpIZ5O8/00-02-21.jpg)
![Screenshot at 03:46: C++ internal code snippet showing TryCatchScope wrapping async\_hooks callbacks with CatchMode::kFatal, explaining why standard error handling fails.](https://ss.rapidrecap.app/screens/y2ujgpIZ5O8/00-03-46.jpg)
![Screenshot at 06:54: A code snippet detailing 'The Fix' in C++, showing the addition of an if \(IsStackOverflowError\) check within the fatal catch scope to re-throw the error instead of exiting the process.](https://ss.rapidrecap.app/screens/y2ujgpIZ5O8/00-06-54.jpg)
