inspector: abort (no message) when the main thread posts to an exiting Worker's connectToMainThread() session

未关闭
#67,079 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

还没有人认领这个 Issue。

评估

难度
4/5
预计耗时
3-5 天
新手友好度
45/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
javascript

调研方向

Start with src/inspector/main_thread_interface.cc, especially MainThreadHandle::Post, MainThreadInterface::Post, and the destructor that resets the handle; read NodeInspectorClient ownership in inspector_agent.cc as well. Run test/parallel/test-inspector-connect-to-main-thread.js and inspect its connect, disconnect, and exit sequence. Done means posts racing with Worker shutdown are tolerated without aborting.

由索引模型根据 Issue 内容生成。

描述

Version

v24.21.0, v24.22.1. src/inspector/main_thread_interface.cc is unchanged through v26.12.1 and main (ec7a28f2).

Platform

Linux x64 (Ubuntu 24.04), official nodejs.org binaries

Subsystem

inspector, worker

What steps will reproduce the bug?

A Worker opens new inspector.Session().connectToMainThread() and exits while the main thread still has something to post back to that session: a protocol reply or event, or the delete it sends when the main-thread side of the session is destroyed after session.disconnect().

test/parallel/test-inspector-connect-to-main-thread.js is the minimal form: connect, disconnect, exit.

How often does it reproduce? Is there a required condition?

Rarely. It needs a narrow native interleaving, and there is no deterministic JS reproduction. Seen twice in CI, in a suite that repeatedly connects, pauses and disconnects Worker sessions to the main thread. Node's CI has seen the test above crash with exit code -6 and no output (#54804, closed as a duplicate of #40611). With no native stack there, I can't confirm it is this bug.

Required condition: the main thread posts to the Worker's handle, while the Worker's request queue is empty, after the Worker's MainThreadInterface use count reaches zero and before ~MainThreadInterface resets the handle.

What is the expected behavior? Why is that the expected behavior?

No crash. Posting to a thread that is going away is meant to be tolerated: MainThreadHandle::Post returns false once the handle is reset (main_thread_interface.cc#L317-L323), and ~AnotherThreadObjectReference posts unconditionally, noting "Disappearing thread may cause a memory leak" (#L101-L104). The test above says "Do not crash".

What do you see instead?

SIGABRT, with no Node error message. Main thread, v24.21.0 (gdb on the core):

#2  abort ()
#3  node::inspector::MainThreadInterface::Post(...) [clone .cold]
#4  node::inspector::(anonymous namespace)::ThreadSafeDelegate::SendMessageToFrontend(...)
#5  node::inspector::(anonymous namespace)::ChannelImpl::sendResponse(...)
...
#8  v8_inspector::protocol::Debugger::DomainDispatcherImpl::pause(...)
...
#14 node::inspector::MainThreadInterface::DispatchMessages() [clone .part.0]

The Worker's thread at the same moment:

#2  uv_mutex_lock
#3  node::inspector::MainThreadInterface::~MainThreadInterface()
#4  std::_Sp_counted_base<...>::_M_release_last_use_cold()
#5  node::inspector::NodeInspectorClient::~NodeInspectorClient()
...
#7  node::Environment::~Environment()
#9  node::FreeEnvironment(node::Environment*)
#10 node::worker::Worker::Run()

On v24.22.1 the Worker's thread is the same, and the main thread's post comes from the session's deletion instead: MainThreadInterface::RemoveObject → ~SameThreadInspectorSession → ~ChannelImpl → ~ThreadSafeDelegate → MainThreadInterface::Post [clone .cold] → abort.

Additional information

Cause, at ec7a28f2:

  • MainThreadHandle::Post holds block_lock_ and forwards only while main_thread_ is set (L317-L323). main_thread_ is cleared only by ~MainThreadInterface (L202-L205), which runs after the interface's only long-lived owner, NodeInspectorClient::interface_ (inspector_agent.cc#L840), has dropped the use count to zero.
  • In that window, MainThreadInterface::Post calls shared_from_this() when its queue is empty (L213). Node builds with -fno-exceptions (common.gypi#L605), so the expired shared_from_this() aborts instead of throwing std::bad_weak_ptr. In the official v24.21.0 linux-x64 binary, Post's cold block (0x7812c0: call abort@plt) is reached only from that call's two bad_weak_ptr branches, and the captured return address is 0x7812c5. The Worker above is waiting in Reset() for block_lock_, which the main thread holds.
  • disconnect() returns once the delete is queued on the main thread (the delegate is "destroyed asynchronously", #46942), and nothing tells the Worker when the main thread is done with the session. A Worker that exits right after disconnect(), as the test does, can hit this.

Every cross-thread post to a Worker's interface goes through these two functions, so a fix there covers the reply, event and delete paths. Fix idea: reset the handle while the interface is still owned, for example in ~NodeInspectorClient before interface_ is released, so a racing post takes the existing return false path. Using weak_from_this() at L213 would also remove the abort.

Disclosure: this report was drafted and posted by an AI coding agent (Claude Code) at the direction of @nick-inkeep, who decided to file it. Before posting, a second, independent agent re-checked every linked source line at ec7a28f2, the duplicate searches, both stacks (symbol-mapped against the official v24.21.0 and v24.22.1 linux-x64 binaries) and the disassembly.

主要语言
JavaScript
星标
123k
派生
39.3k
平均合并
4 天 15 小时
30 天内合并 PR
238

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

nodejs/node 的其他 Issue

查看 nodejs/node 的全部 Issue

相似的 Issue

更多 JavaScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。