Intermittent SIGSEGV in Maglev concurrent compilation on Linux (PersistentHandles::GetHandle) — regression between v24.14.1 and v24.15.0
还没有人认领这个 Issue。
- 主要语言
- JavaScript
- 星标
- 122k
- 派生
- 37.4k
- 平均合并
- 4 天 3 小时
- 30 天内合并 PR
- 272
描述
Version
v24.15.0 through v24.18.1 (current LTS). v24.14.1 is not affected.
Platform
Linux 6.8.0-136-generic x86_64 (Ubuntu 24.04.4, glibc 2.39) — also observed on GitHub Actions ubuntu-latest. Not the Windows-only issue from #62991 (that was fixed in v24.16.0; this reproduces on Linux through v24.18.1).
Subsystem
v8 (Maglev)
What steps will reproduce the bug?
The trigger is CPU-hot JS tiering up to Maglev while the machine is CPU-starved, so the repro pins several node processes to two cores. I hit it with an undici test that runs tight request/teardown loops, but the crash is in V8's concurrent compiler thread and has nothing to do with undici's native surface (no addons; the crashing thread is a Maglev compile job):
git clone https://github.com/nodejs/undici.git && cd undici # tested at c76fe46e
npm i
for i in $(seq 1 6); do
(taskset -c 0,1 node test/http2-request-never-settles.js >/dev/null 2>&1; echo "run $i: exit $?") &
done; wait
Repeat the batch a few times; on v24.18.x roughly 1 in 5–10 processes exits 139 (SIGSEGV) with no output.
How often does it reproduce? Is there a required condition?
With 6 processes pinned to 2 cores (48 runs per version, same machine):
| Node | V8 | SIGSEGV rate |
|---|---|---|
| v24.14.1 | 13.6.233.17-node.44 | 0/96 |
| v24.16.0 | 13.6.233.17-node.49 | 1/48 |
| v24.18.0 | 13.6.233.17-node.50 | 9/48 |
| v24.18.1 | 13.6.233.17-node.50 | 5/48 |
v24.18.1 --no-maglev |
13.6.233.17-node.50 | 0/48 |
CPU contention is the required condition — on an idle machine with a single process it passes reliably. --no-maglev eliminates the crash entirely.
The regression window is the V8 cherry-picks between 13.6.233.17-node.44 (v24.14.1) and -node.48/49 (v24.15.0/v24.16.0), with the rate increasing at -node.50.
What is the expected behavior? Why is that the expected behavior?
The process should not segfault.
What do you see instead?
The child dies on SIGSEGV with no output at all, which surfaces in node:test/borp-based CI as the file-level ✖ ... 'test failed' with no failing subtest — easy to misread as a flaky test. Real-world sighting on undici's CI (Node 24.18.0, ubuntu-latest): https://github.com/nodejs/undici/actions/runs/30488013544/job/90698464241?pr=5623
Native stack captured with segfault-handler, identical in 6 of 7 crashes (addresses symbolized with addr2line against the release binary):
PID 31631 received SIGSEGV for address: 0x10013a01aa0
v8::internal::PersistentHandles::GetHandle(unsigned long)
v8::internal::compiler::BytecodeArrayRef::SourcePositionTable(v8::internal::compiler::JSHeapBroker*) const
v8::internal::maglev::MaglevGraphBuilder::MaglevGraphBuilder(v8::internal::LocalIsolate*, v8::internal::maglev::MaglevCompilationUnit*, v8::internal::maglev::Graph*, v8::internal::maglev::MaglevCallerDetails*)
v8::internal::maglev::MaglevCompiler::Compile(v8::internal::LocalIsolate*, v8::internal::maglev::MaglevCompilationInfo*)
v8::internal::maglev::MaglevCompilationJob::ExecuteJobImpl(v8::internal::RuntimeCallStats*, v8::internal::LocalIsolate*)
v8::internal::OptimizedCompilationJob::ExecuteJob(v8::internal::RuntimeCallStats*, v8::internal::LocalIsolate*)
v8::internal::maglev::MaglevConcurrentDispatcher::JobTask::Run(v8::JobDelegate*)
v8::platform::DefaultJobWorker::Run()
node::(anonymous namespace)::PlatformWorkerThread(void*)
The faulting addresses look like out-of-cage pointers (0x10013a01aa0, 0x1004128d270), consistent with a bogus persistent-handle index/slot read on the background thread.
One outlier crash (1 of 7) was a null deref on the main thread instead:
PID 32143 received SIGSEGV for address: 0x0
node::CallbackQueue<...>::CallbackImpl<node::crypto::TLSWrap::EncOut()::{lambda(node::Environment*)#2}>::Call(node::Environment*)
node::Environment::RunAndClearNativeImmediates(bool)
node::Environment::CheckImmediate(uv_check_t*)
uv_run
Possibly fallout from the same corruption, but noting it in case it's a separate race.
Additional information
The workload that triggers it is pure JS churn: an HTTP/2 client/server in-process with rapid stream resets, GOAWAYs and session teardown, i.e. lots of short-lived hot functions being tiered up concurrently. Happy to help narrow the V8 cherry-pick range or test candidate fixes.
Disclaimer: I created this report with the help of AI.
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
调研方向
首先使用 undici 工作负载 test/http2-request-never-settles.js 运行 pinned-core 复现,比较 v24.14.1 到 v24.18.1,以及 --no-maglev 的结果。阅读 PersistentHandles::GetHandle 附近的 V8 Maglev 并发编译路径和报告的堆栈,然后缩小相关 V8 cherry-pick 的范围。当压力复现不再在 Linux 上发生段错误,同时 Maglev 仍保持启用时,即视为完成。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- javascript, nodejs
- 领域
- backend, compilers, operating-systems
- Issue 类型
- 缺陷
- 难度
- 5/5
- 预计耗时
- 一周以上
- 活跃度
- 冷清
- 描述清晰度
- 基本清楚
- 新手友好度
- 35/100