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

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 Database, 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: Database::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 4 times, most recently 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.37%. Comparing base (d35c89e) to head (35ed41b).
⚠️ Report is 10 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.36%   90.37%            
========================================
  Files         790      790            
  Lines      274657   274816   +159     
  Branches    52587    52638    +51     
========================================
+ Hits       248207   248373   +166     
+ Misses      16935    16923    -12     
- Partials     9515     9520     +5     
Files with missing lines Coverage Δ
src/node_diagnostics_channel.h 71.42% <ø> (+14.28%) ⬆️
src/node_sqlite.h 87.27% <ø> (ø)
src/node_diagnostics_channel.cc 84.18% <87.50%> (+0.53%) ⬆️
src/node_sqlite.cc 81.96% <50.00%> (ø)

... and 30 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
@bengl bengl added the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Sep 23, 2026
@panva panva added the author ready PRs with CI started, the required approvals, and no outstanding review comments. label Sep 24, 2026
@github-actions github-actions Bot removed the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Sep 24, 2026
@nodejs-github-bot

This comment was marked as outdated.

@panva panva added the resume-ci Add this label to resume the latest eligible Jenkins CI run on a PR with an approving review. label Sep 25, 2026
@github-actions github-actions Bot removed the resume-ci Add this label to resume the latest eligible Jenkins CI run on a PR with an approving review. label Sep 25, 2026
@nodejs-github-bot

This comment was marked as outdated.

@panva panva added the resume-ci Add this label to resume the latest eligible Jenkins CI run on a PR with an approving review. label Sep 25, 2026
@github-actions github-actions Bot removed the resume-ci Add this label to resume the latest eligible Jenkins CI run on a PR with an approving review. label Sep 25, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@github-actions github-actions Bot removed the author ready PRs with CI started, the required approvals, and no outstanding review comments. label Sep 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This pull request has conflicts with its base branch, removing the author ready label.
Please rebase your branch onto the latest base branch, resolve the conflicts locally, and force-push.
Afterwards the pull request needs a fresh collaborator approval, and a collaborator will add the label back once it is author ready again.

`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 `Database`, 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 `Database::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. `Statement::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 4d9852c to 35ed41b Compare September 26, 2026 21:04
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

5 participants