Skip to content

diagnostics_channel: fix dangling binding pointer - #65860

Open
TrevorBurnham wants to merge 1 commit into
nodejs:mainfrom
TrevorBurnham:dc-clear-channel-binding-data
Open

diagnostics_channel: fix dangling binding pointer#65860
TrevorBurnham wants to merge 1 commit into
nodejs:mainfrom
TrevorBurnham:dc-clear-channel-binding-data

Conversation

@TrevorBurnham

@TrevorBurnham TrevorBurnham commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Fixes: #65858

diagnostics_channel's Channel reads its subscriber count through a raw BindingData* that's never cleared, so any native holder that outlives environment cleanup reads a destroyed object. That impacts node:sqlite's DatabaseSync, which holds a strong BaseObjectPtr<Channel> for its lifetime, leading to a potential segfault.

This PR clears binding_data_ on every Channel the binding owns whenever it gives up that ownership, so the existing null check in HasSubscribers() can do its job.

Two changes go to node:sqlite as well: DatabaseSync::trace_channel_ becomes a BaseObjectWeakPtr, matching the convention permission documents and follows, where BindingData is the sole owner of channels. And TraceCallback now tests AreTraceEventsSuppressed() before the channel, so a suppressed callback doesn't dereference it at all.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/sqlite

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. needs-ci PRs that need a full CI run. labels Sep 6, 2026
@TrevorBurnham
TrevorBurnham force-pushed the dc-clear-channel-binding-data branch 3 times, most recently from ff5512d to 7db0a64 Compare September 6, 2026 18:14
`Channel` reads its subscriber count through a raw `BindingData*` that
was never cleared, so any native holder that outlives environment
cleanup reads a destroyed object. The null check in `HasSubscribers()`
could not fire, because the pointer was only ever assigned in the
constructor.

`node:sqlite` holds a strong `BaseObjectPtr<Channel>` for the lifetime
of a `DatabaseSync`, which made this reachable from ordinary
JavaScript. A statement left mid-step at exit is finalized by the
destructor chain after `Environment::RunCleanup()` has destroyed the
binding, and `sqlite3_finalize()` invokes the profile callback for such
a statement. The result was a segfault at normal process exit; inside a
worker it took down the whole process.

Clear `binding_data_` on every `Channel` the binding owns whenever it
gives up that ownership, both in the destructor and in
`PrepareForSerialization()`, so that the existing null check in
`HasSubscribers()` does its job. The second check in `Publish()` is now
unreachable and is dropped. This protects any holder that is itself a
`BaseObject`, and so is destroyed later in the same cleanup. A holder
that is not a `BaseObject` still needs a cleanup hook or a weak
reference, because `Realm::~Realm()` checks that no `BaseObject`s
remain.

On the `node:sqlite` side, switch `DatabaseSync::trace_channel_` to a
`BaseObjectWeakPtr`, so that it follows the same convention `permission`
documents, where `BindingData` is the sole owner of channels.
`TraceCallback` already null-checks, so this needs no other change
there.

Also check `AreTraceEventsSuppressed()` before the channel in
`TraceCallback()`, so that a suppressed callback does not dereference it
at all. `StatementSync::Finalize()` already suppresses trace events, so
the reported path was meant to be a no-op; only the order of the `||`
operands took it through the channel first.

Fixes: nodejs#65858
Assisted-by: Claude Opus 5
Signed-off-by: Trevor Burnham <trevorburnham@gmail.com>
@TrevorBurnham
TrevorBurnham force-pushed the dc-clear-channel-binding-data branch from 7db0a64 to 4d9852c Compare September 6, 2026 21:49
@TrevorBurnham
TrevorBurnham marked this pull request as ready for review September 6, 2026 22:39
@codecov

codecov Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.00000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.19%. Comparing base (7147a3e) to head (4d9852c).
⚠️ Report is 23 commits behind head on main.

Files with missing lines Patch % Lines
src/node_diagnostics_channel.cc 87.50% 0 Missing and 1 partial ⚠️
src/node_sqlite.cc 50.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #65860      +/-   ##
==========================================
+ Coverage   90.18%   90.19%   +0.01%     
==========================================
  Files         771      771              
  Lines      264619   264626       +7     
  Branches    50224    50231       +7     
==========================================
+ Hits       238643   238682      +39     
+ Misses      16959    16949      -10     
+ Partials     9017     8995      -22     
Files with missing lines Coverage Δ
src/node_diagnostics_channel.h 71.42% <ø> (+14.28%) ⬆️
src/node_sqlite.h 86.40% <ø> (ø)
src/node_diagnostics_channel.cc 84.18% <87.50%> (+0.53%) ⬆️
src/node_sqlite.cc 82.20% <50.00%> (ø)

... and 23 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Flarna Flarna added the diagnostics_channel Issues and PRs related to the diagnostics_channel module. label Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++ Issues and PRs that require attention from people who are familiar with C++. diagnostics_channel Issues and PRs related to the diagnostics_channel module. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

diagnostics_channel: Channel::binding_data_ dangles after environment cleanup, crashing node:sqlite at exit

3 participants