diff --git a/src/node.cc b/src/node.cc index 5e00996c1ba..b4e8cdaf2aa 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 00000000000..14e2573328b --- /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/); +}