Worker with large inline source map aborts process in V8 HandleDebugMagicComments
まだ誰も着手していません。
- 主要言語
- JavaScript
- スター
- 122k
- フォーク
- 37.3k
- 平均マージ
- 4日 2時間
- マージ済み PR(30日)
- 283
説明
Version
v26.4.0
Platform
macOS 15.x arm64
Subsystem
worker_threads
What steps will reproduce the bug?
Create a worker entry file with a small amount of executable JavaScript and a very large inline sourceMappingURL=data:... comment, then load it in a worker with a constrained heap.
Minimal repro:
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const { Worker } = require("node:worker_threads");
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "node-inline-sourcemap-oom-"));
const workerPath = path.join(dir, "worker.cjs");
const source = [
'const { parentPort } = require("node:worker_threads");',
'parentPort.postMessage("loaded");',
'parentPort.close();',
"",
"//# sourceMappingURL=data:application/json;base64,",
"A".repeat(64 * 1024 * 1024),
"",
].join("\n");
fs.writeFileSync(workerPath, source);
const worker = new Worker(workerPath, {
resourceLimits: {
maxOldGenerationSizeMb: 16,
},
});
worker.on("message", console.log);
worker.on("error", console.error);
worker.on("exit", (code) => {
console.log("exit", code);
fs.rmSync(dir, { recursive: true, force: true });
});
Run: node repro.js
How often does it reproduce? Is there a required condition?
It reproduces consistently when the inline sourcemap comment is large enough relative to the worker heap limit.
For comparison, these do not reproduce the same fatal abort:
- Keeping the same large payload in an external worker.cjs.map file and using //# sourceMappingURL=worker.cjs.map.
- Putting a similarly large payload in a normal non-sourceMappingURL comment.
What is the expected behavior? Why is that the expected behavior?
The worker should fail gracefully, for example by emitting an error event or exiting with a worker failure, without aborting the entire Node.js process.
Ideally, parsing sourceMappingURL / debug magic comments should not internalize an arbitrarily large data URL into V8 old space during module compilation.
What do you see instead?
The whole Node process aborts with an out-of-memory fatal error before the worker can load the module.
Example stack from Node v26.4.0:
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
...
v8::internal::FactoryBase<v8::internal::Factory>::AllocateRawOneByteInternalizedString
v8::internal::FactoryBase<v8::internal::Factory>::NewOneByteInternalizedString
v8::internal::StringTable::LookupKey
v8::internal::FactoryBase<v8::internal::Factory>::InternalizeString
void v8::internal::Parser::HandleDebugMagicComments
v8::internal::Parser::ParseProgram
v8::internal::parsing::ParseProgram
v8::internal::Compiler::GetWrappedFunction
v8::ScriptCompiler::CompileFunction
node::contextify::CompileFunctionForCJSLoader
node::worker::Worker::Run
Additional information
No response
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
提供された repro.js と V8's Parser::HandleDebugMagicComments を通るスタックから始め、次に worker heap limit 下で inline source map と外部 source map を比較します。node::worker::Worker::Run を通じて worker のエントリを追跡し、致命的な割り当てが発生する場所を特定します。repro が Node.js プロセスを中断せず、worker が失敗を正常に報告すれば完了です。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- javascript
- 領域
- backend
- issue の種類
- バグ
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 活発さ
- 静か
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 42/100