From d1960949bb0710df25ca00320b2e51a69ef73238 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sat, 19 Sep 2026 17:20:15 +0000 Subject: [PATCH] benchmark: protect against accidental fork bomb Signed-off-by: James M Snell Assisted-by: Opencode PR-URL: https://github.com/nodejs/node/pull/66132 Reviewed-By: Rafael Gonzaga Reviewed-By: Matteo Collina --- benchmark/common.js | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/benchmark/common.js b/benchmark/common.js index 8443da40d79..fa108b70683 100644 --- a/benchmark/common.js +++ b/benchmark/common.js @@ -198,6 +198,21 @@ class Benchmark { } _run() { + // A forked child is told to run the benchmark function directly, rather + // than build its own queue and fork again, through the + // NODE_RUN_BENCHMARK_FN environment variable. A child always inherits + // this.flags in its execArgv, so reaching _run() with those flags already + // applied means the variable did not survive to the child and every + // generation would keep forking. Fail loudly instead of forking forever. + if (process.send && + this.flags.length > 0 && + this.flags.every((flag) => process.execArgv.includes(flag))) { + throw new Error( + 'Benchmark child process was started with the benchmark flags but ' + + 'without NODE_RUN_BENCHMARK_FN, refusing to fork again. Something ' + + 'removed the variable from the child environment.'); + } + // If forked, report to the parent. if (process.send) { process.send({ @@ -213,6 +228,20 @@ class Benchmark { this.originalOptions.setup(this.queue); } + // Enforcing the permission model removes the environment variables + // --allow-env does not grant access to at startup, which would drop the + // NODE_RUN_BENCHMARK_FN set below. The child only ever sees the + // environment this process hands it, so granting access to all of it does + // not widen what the benchmark can reach. Audit mode removes nothing, so + // it is left alone to keep its diagnostics intact. + const childExecArgv = this.flags.concat(process.execArgv); + const enforcesPermission = (arg) => + arg === '--permission' || arg.startsWith('--permission='); + if (childExecArgv.some(enforcesPermission) && + !childExecArgv.some((arg) => arg.startsWith('--allow-env'))) { + childExecArgv.push('--allow-env=*'); + } + const recursive = (queueIndex) => { const config = this.queue[queueIndex]; @@ -233,7 +262,7 @@ class Benchmark { const child = child_process.fork(require.main.filename, childArgs, { env: childEnv, - execArgv: this.flags.concat(process.execArgv), + execArgv: childExecArgv, }); child.on('message', sendResult); child.on('close', (code) => {