From efc393625496a7467550edfbc4af4bde3aaeff1f Mon Sep 17 00:00:00 2001 From: Paul Bouchon Date: Thu, 27 Aug 2026 08:51:13 -0400 Subject: [PATCH] src: disable V8 external memory reasonable size check V8 aborts the process when external memory grows by more than --external-memory-max-reasonable-size gigabytes in a single step. The 32 GB default is a Chromium-oriented sanity check; allocating a buffer larger than that is legitimate in Node and should raise a RangeError rather than terminate the process. Default the flag to 0 unless the user supplied a value, alongside the other V8 defaults Node sets explicitly. Fixes: https://github.com/nodejs/node/issues/65534 Signed-off-by: Paul Bouchon --- src/node.cc | 14 ++++++++ .../test-external-memory-reasonable-size.js | 33 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 test/parallel/test-external-memory-reasonable-size.js diff --git a/src/node.cc b/src/node.cc index 5e00996c1ba3..b4e8cdaf2aaa 100644 --- a/src/node.cc +++ b/src/node.cc @@ -777,6 +777,20 @@ static ExitCode ProcessGlobalArgsInternal(std::vector* args, v8_args.emplace_back("--js-source-phase-imports"); } + // V8 aborts the process when external memory grows by more than + // --external-memory-max-reasonable-size gigabytes in a single step. That + // limit is a Chromium-oriented sanity check; allocating a buffer larger + // than it is a legitimate thing to do in Node, and should raise a + // RangeError rather than crash. Disable the check unless the user asked + // for a specific limit. + // Refs: https://github.com/nodejs/node/issues/65534 + if (std::ranges::none_of(v8_args, [](const std::string& arg) { + return arg.starts_with("--external-memory-max-reasonable-size") || + arg.starts_with("--external_memory_max_reasonable_size"); + })) { + v8_args.emplace_back("--external-memory-max-reasonable-size=0"); + } + #ifdef __POSIX__ // Block SIGPROF signals when sleeping in epoll_wait/kevent/etc. Avoids the // performance penalty of frequent EINTR wakeups when the profiler is running. diff --git a/test/parallel/test-external-memory-reasonable-size.js b/test/parallel/test-external-memory-reasonable-size.js new file mode 100644 index 000000000000..14e2573328ba --- /dev/null +++ b/test/parallel/test-external-memory-reasonable-size.js @@ -0,0 +1,33 @@ +'use strict'; + +// V8 aborts the process when external memory grows by more than +// --external-memory-max-reasonable-size gigabytes in a single step. Node +// disables that check by default, but an explicit value on the command line +// must still be honored. +// Refs: https://github.com/nodejs/node/issues/65534 + +const common = require('../common'); +const assert = require('assert'); +const { spawnSync } = require('child_process'); +const { totalmem } = require('os'); + +// The smallest limit V8 accepts is 1 GB, so the child has to allocate more +// than that before the check can fire. +if (totalmem() < 4 * 1024 ** 3) + common.skip('not enough memory to exceed a 1 GB external memory limit'); + +for (const flag of [ + '--external-memory-max-reasonable-size=1', + '--external_memory_max_reasonable_size=1', +]) { + const child = spawnSync(process.execPath, [ + flag, '-e', 'new Float64Array(150_000_000)', + ]); + + assert.notStrictEqual( + child.status, + 0, + `${flag} was not honored, the child exited cleanly`, + ); + assert.match(child.stderr.toString(), /kMaxReasonableBytes/); +}