Skip to content

src: keep the first snapshot blob alive for later isolates - #65779

Open
codebytere wants to merge 2 commits into
nodejs:mainfrom
codebytere:fix/embedder-snapshot-first-params-uaf
Open

src: keep the first snapshot blob alive for later isolates#65779
codebytere wants to merge 2 commits into
nodejs:mainfrom
codebytere:fix/embedder-snapshot-first-params-uaf

Conversation

@codebytere

@codebytere codebytere commented Sep 4, 2026

Copy link
Copy Markdown
Member

An embedder that creates a CommonEnvironmentSetup from an EmbedderSnapshotData, tears it down, releases the snapshot data and later creates a second setup hands V8 freed memory, because every isolate in a process is created from the first isolate's snapshot blob and NewIsolate() only kept a pointer to the first caller's CreateParams; ASAN reports a use-after-free in Snapshot::Initialize. A second commit fixes a race in the same first-isolate setup: two threads creating their first isolate at once could both finalize the external reference list.

NewIsolate() now records the first blob and external references under a mutex and ~SnapshotData() leaves that one blob allocated; the external reference list is finalized once in a function-local static. Nothing is copied and node itself is unaffected. node.h now states the lifetime snapshot_data needs.

Tests: test/embedding/test-embedding-snapshot-twice.js (use-after-free under ASAN before) and EnvironmentTest.CollectExternalReferencesFromSeveralThreads (TSAN before); the rest of test/embedding passes.

Refs: #45885
Refs: #32984


Disclosure: the code, tests and this description were written by Claude Code, directed and reviewed by @codebytere.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/startup

@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 4, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@codecov

codecov Bot commented Sep 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.83333% with 1 line in your changes missing coverage. Please review.
βœ… Project coverage is 90.18%. Comparing base (1e0ebef) to head (6970520).
⚠️ Report is 34 commits behind head on main.

Files with missing lines Patch % Lines
src/node_snapshotable.cc 80.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #65779      +/-   ##
==========================================
+ Coverage   90.17%   90.18%   +0.01%     
==========================================
  Files         771      771              
  Lines      265097   265116      +19     
  Branches    50362    50362              
==========================================
+ Hits       239054   239103      +49     
+ Misses      17004    16961      -43     
- Partials     9039     9052      +13     
Files with missing lines Coverage Ξ”
src/api/environment.cc 78.98% <100.00%> (+1.12%) ⬆️
src/node.h 91.66% <ΓΈ> (ΓΈ)
src/node_internals.h 80.35% <ΓΈ> (ΓΈ)
src/node_snapshotable.cc 73.55% <80.00%> (+0.08%) ⬆️

... and 37 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.

@nodejs-github-bot

This comment was marked as outdated.

@nodejs-github-bot

nodejs-github-bot commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator

@panva panva added the author ready PRs with CI started, the required approvals, and no outstanding review comments. label Sep 7, 2026
`NewIsolate()` creates every isolate from the snapshot blob the first
isolate in the process used, because V8 shares the read-only heap
between isolates, and did so by keeping a pointer to the first
`CreateParams`. When that blob came from an `EmbedderSnapshotData` the
embedder had since released, e.g. a second
`CommonEnvironmentSetup::CreateFromSnapshot()` after the first setup and
its snapshot were destroyed, V8 deserialized freed memory.

Record the first blob and external references under a mutex instead of
copying the caller's `CreateParams`, and make `~SnapshotData()` leave
that one blob allocated, since its owner can go away before the last
isolate is created. Nothing is copied and `node` itself is unaffected.
embedtest grows an `--embedder-run-twice` switch so the sequence can be
tested.

Refs: nodejs#45885
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
Two threads creating their first isolate at the same time (two
`CommonEnvironmentSetup`s on their own threads, or an embedder's setup
racing a Worker) could corrupt or misread the external reference list
handed to V8: `SnapshotBuilder::CollectExternalReferences()` creates its
registry in a thread-safe function static, but then calls
`external_references()` on every call, and that method appends the
terminating nullptr and flips `is_finalized_` the first time through
without any locking, so both threads can append, or one can read the
vector while the other reallocates it. TSAN reports it for any two
concurrent setups.

Keep the finalized list in a second function static so finalization
runs exactly once, under that static's initialization guard.

Refs: nodejs#32984
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
@codebytere
codebytere force-pushed the fix/embedder-snapshot-first-params-uaf branch from 0cda701 to 6970520 Compare September 7, 2026 22:00
@codebytere codebytere 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 7, 2026
@jasnell jasnell added commit-queue PRs queued for automated landing through the Commit Queue. commit-queue-rebase PRs the Commit Queue should land as multiple self-contained commits. labels Sep 8, 2026
@nodejs-github-bot nodejs-github-bot added commit-queue-failed PRs whose Commit Queue landing failed and need manual intervention before retrying. and removed commit-queue PRs queued for automated landing through the Commit Queue. labels Sep 8, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Commit Queue failed

   ⚠  Commits were pushed since the last approving review:
   ⚠  - src: keep the first snapshot blob alive for later isolates
   ⚠  - src: fix external reference list race between concurrent isolates
   ✘  This PR needs to wait 63 more hours to land (or 0 minutes if there is one more approval)
   ⚠  Commits were pushed after the last Full PR CI run:
   ⚠  - src: keep the first snapshot blob alive for later isolates
   ⚠  - src: fix external reference list race between concurrent isolates

The pull request was removed from the Commit Queue and labeled commit-queue-failed PRs whose Commit Queue landing failed and need manual intervention before retrying. . After resolving the failure, remove that label and add commit-queue PRs queued for automated landing through the Commit Queue. to retry.

Full Commit Queue output
- Loading data for nodejs/node/pull/65779
βœ”  Done loading data for nodejs/node/pull/65779
----------------------------------- PR info ------------------------------------
Title      src: keep the first snapshot blob alive for later isolates (#65779)
Author     Shelley Vohr <shelley.vohr@gmail.com> (@codebytere)
Branch     codebytere:fix/embedder-snapshot-first-params-uaf -> nodejs:main
Labels     c++, author ready, needs-ci, request-ci, commit-queue, commit-queue-rebase
Commits    2
 - src: keep the first snapshot blob alive for later isolates
 - src: fix external reference list race between concurrent isolates
Committers 1
 - Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/65779
Refs: https://github.com/nodejs/node/pull/45885
Refs: https://github.com/nodejs/node/pull/32984
Reviewed-By: James M Snell <jasnell@gmail.com>
------------------------------ Generated metadata ------------------------------
PR-URL: https://github.com/nodejs/node/pull/65779
Refs: https://github.com/nodejs/node/pull/45885
Refs: https://github.com/nodejs/node/pull/32984
Reviewed-By: James M Snell <jasnell@gmail.com>
--------------------------------------------------------------------------------
   ⚠  Commits were pushed since the last approving review:
   ⚠  - src: keep the first snapshot blob alive for later isolates
   ⚠  - src: fix external reference list race between concurrent isolates
   β„Ή  This PR was created on Fri, 04 Sep 2026 08:07:31 GMT
   βœ”  Approvals: 1
   βœ”  - James M Snell (@jasnell) (TSC): https://github.com/nodejs/node/pull/65779#pullrequestreview-5126330640
   ✘  This PR needs to wait 63 more hours to land (or 0 minutes if there is one more approval)
   βœ”  Last GitHub CI successful
   β„Ή  Last Full PR CI on 2026-09-06T17:10:38Z: https://ci.nodejs.org/job/node-test-pull-request/77097/
   ⚠  Commits were pushed after the last Full PR CI run:
   ⚠  - src: keep the first snapshot blob alive for later isolates
   ⚠  - src: fix external reference list race between concurrent isolates
- Querying data for job/node-test-pull-request/77097/
βœ”  Build data downloaded
   βœ”  Last Jenkins CI successful
--------------------------------------------------------------------------------
   βœ”  Aborted `git node land` session in /home/runner/work/node/node/.ncu

View workflow run

@jasnell jasnell removed the commit-queue-failed PRs whose Commit Queue landing failed and need manual intervention before retrying. label Sep 8, 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 8, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author ready PRs with CI started, the required approvals, and no outstanding review comments. c++ Issues and PRs that require attention from people who are familiar with C++. commit-queue-rebase PRs the Commit Queue should land as multiple self-contained commits. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants