nodejs / nodejs/node

Intermittent SIGSEGV in Maglev concurrent compilation on Linux (PersistentHandles::GetHandle) — regression between v24.14.1 and v24.15.0

Đang mở
#64,841 5 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

http2
Ngôn ngữ chính
JavaScript
Star
122k
Fork
37.3k
Merge trung bình
4 ngày 2 giờ
Pull request đã merge (30 ngày)
283

Mô tả

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.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Hướng nghiên cứu

Bắt đầu bằng cách chạy bản tái hiện pinned-core với workload undici test/http2-request-never-settles.js, so sánh từ v24.14.1 đến v24.18.1 và kết quả với --no-maglev. Đọc đường dẫn biên dịch đồng thời của V8 Maglev quanh PersistentHandles::GetHandle và stack được báo cáo, sau đó thu hẹp các cherry-pick V8 liên quan. Được xem là hoàn tất khi bản tái hiện stress không còn gây segfault trên Linux trong khi Maglev vẫn được bật.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
javascript, nodejs
Lĩnh vực
backend, compilers, operating-systems
Loại issue
Lỗi
Độ khó
5/5
Thời gian dự kiến
Hơn một tuần
Mức độ hoạt động
Ít trao đổi
Độ rõ ràng
Khá rõ ràng
Mức phù hợp với người mới
35/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.