node/test/parallel/test-async-hooks-stack-overflow-try-catch.js
Matteo Collina 73747597e0
src: rethrow stack overflow exceptions in async_hooks
When a stack overflow exception occurs during async_hooks callbacks
(which use TryCatchScope::kFatal), detect the specific "Maximum call
stack size exceeded" RangeError and re-throw it instead of immediately
calling FatalException. This allows user code to catch the exception
with try-catch blocks instead of requiring uncaughtException handlers.

The implementation adds IsStackOverflowError() helper to detect stack
overflow RangeErrors and re-throws them in TryCatchScope destructor
instead of calling FatalException.

This fixes the issue where async_hooks would cause stack overflow
exceptions to exit with code 7 (kExceptionInFatalExceptionHandler)
instead of being catchable.

Fixes: https://github.com/nodejs/node/issues/37989
Ref: https://hackerone.com/reports/3456295
PR-URL: https://github.com/nodejs-private/node-private/pull/773
Refs: https://hackerone.com/reports/3456295
Reviewed-By: Robert Nagy <ronagy@icloud.com>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
CVE-ID: CVE-2025-59466
2026-01-13 17:02:27 +01:00

47 lines
1.4 KiB
JavaScript

'use strict';
// This test verifies that when a stack overflow occurs with async_hooks
// enabled, the exception can be caught by try-catch blocks in user code.
require('../common');
const assert = require('assert');
const { spawnSync } = require('child_process');
if (process.argv[2] === 'child') {
const { createHook } = require('async_hooks');
createHook({ init() {} }).enable();
function recursive(depth = 0) {
// Create a promise to trigger async_hooks init callback
new Promise(() => {});
return recursive(depth + 1);
}
try {
recursive();
// Should not reach here
process.exit(1);
} catch (err) {
assert.strictEqual(err.name, 'RangeError');
assert.match(err.message, /Maximum call stack size exceeded/);
console.log('SUCCESS: try-catch caught the stack overflow');
process.exit(0);
}
// Should not reach here
process.exit(2);
} else {
// Parent process - spawn the child and check exit code
const result = spawnSync(
process.execPath,
[__filename, 'child'],
{ encoding: 'utf8', timeout: 30000 }
);
assert.strictEqual(result.status, 0,
`Expected exit code 0 (try-catch worked), got ${result.status}.\n` +
`stdout: ${result.stdout}\n` +
`stderr: ${result.stderr}`);
assert.match(result.stdout, /SUCCESS: try-catch caught the stack overflow/);
}