Skip to content

src: fix use-after-free in CleanupHookThunkRun - #65630

Merged
nodejs-github-bot merged 2 commits into
nodejs:mainfrom
everett1992:test-cleanup-hook-uaf-regression-65196
Sep 4, 2026
Merged

nodejs-github-bot merged 2 commits into
nodejs:mainfrom
everett1992:test-cleanup-hook-uaf-regression-65196

Conversation

@everett1992

@everett1992 everett1992 commented Aug 28, 2026 •

Copy link
Copy Markdown
Contributor

This PR is a continuation of @sreehariannam's work in

#65196

I've added a regression test and fixed the lint issue. I'm only opening this because there's no activity on that PR, and this bug is blocking the backport of #65042 which fixes a crash with better-sqlite3 on node v24.19.0

Fixes: #65195

@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 Aug 28, 2026
@everett1992
everett1992 marked this pull request as ready for review August 29, 2026 16:32
@codecov

codecov Bot commented Aug 29, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.07%. Comparing base (f9ab994) to head (fd325a7).
⚠️ Report is 30 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #65630   +/-   ##
=======================================
  Coverage   90.07%   90.07%           
=======================================
  Files         754      754           
  Lines      256395   256398    +3     
  Branches    48494    48495    +1     
=======================================
+ Hits       230947   230954    +7     
+ Misses      16563    16561    -2     
+ Partials     8885     8883    -2     
Files with missing lines Coverage Δ
src/api/hooks.cc 88.19% <100.00%> (-0.41%) ⬇️

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

@trivikr trivikr changed the title Test cleanup hook uaf regression 65196 src: fix use-after-free in CleanupHookThunkRun Aug 31, 2026
@trivikr trivikr 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 1, 2026
@trivikr
trivikr requested a review from aduh95 September 1, 2026 02:05
@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 1, 2026
@nodejs-github-bot

This comment was marked as outdated.

@nodejs-github-bot

This comment was marked as outdated.

@trivikr

This comment was marked as outdated.

everett1992 and others added 2 commits September 1, 2026 09:53
CleanupHookThunkRun() read thunk->isolate/fun/arg from the
CleanupHookThunk after invoking thunk->fun(). For every
node::ObjectWrap alive at teardown, thunk->fun is
ObjectWrap::CleanupHook, which deletes the wrap; ~ObjectWrap() calls
RemoveEnvironmentCleanupHook() itself, erasing the CleanupHookThunk
from the registry and freeing the node it lives in. The subsequent
read of thunk->isolate/fun/arg to make the (now redundant) second
RemoveEnvironmentCleanupHook() call was therefore a use-after-free.

Cache the fields before running the hook so nothing is read from
`thunk` once it may have been freed.

Taken over from nodejs#65196, which has been inactive; the original change is
unmodified apart from the added comment.

This also unblocks nodejs#65042, the backport of the cleanup hook registry to
v24.x. Without that registry ~ObjectWrap() asserts during garbage
collection, so every 24.x runtime aborts for ObjectWrap addons
(nodejs#65446), as do 26.x runtimes before 26.4.0 when used with newer
headers (nodejs#65262).

Fixes: nodejs#65195
Refs: nodejs#65196
Refs: nodejs#65042
Refs: nodejs#65446
Refs: nodejs#65262
Assisted-by: a closed-source coding agent
Co-authored-by: Sreehari Annam <sreehari.annam@gmail.com>
Signed-off-by: Caleb Everett <everett.caleb@gmail.com>
Add a cctest that registers an environment cleanup hook which removes
itself while the cleanup queue is drained. It exercises
CleanupHookThunkRun(), which must not read the CleanupHookThunk after
invoking the hook, because the hook has already erased and freed it.

The hook is registered directly rather than through node::ObjectWrap.
ObjectWrap is what makes this reachable for addons since nodejs#63642,
because its destructor removes its own hook, and nodejs#65195 reproduces the
fault that way with test/addons/worker-addon-exit. That reproducer
needs an addon build and depends on when the wrapper is collected,
whereas this test drives the self-removal directly.

The use-after-free is silent in ordinary builds and is caught by the
ASan/Valgrind CI, which is how the original assertion (nodejs#63923)
surfaced. Verified locally with an ASan build: without the preceding
commit both this test and test/addons/worker-addon-exit report
heap-use-after-free in CleanupHookThunkRun(); both are clean with it.

Refs: nodejs#65195
Refs: nodejs#65196
Assisted-by: a closed-source coding agent
Co-authored-by: Sreehari Annam <sreehari.annam@gmail.com>
Co-authored-by: nsavoire <19255994+nsavoire@users.noreply.github.com>
Signed-off-by: Caleb Everett <everett.caleb@gmail.com>
@everett1992
everett1992 force-pushed the test-cleanup-hook-uaf-regression-65196 branch from bb42c6d to fd325a7 Compare September 1, 2026 16:55
@trivikr trivikr 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 1, 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 1, 2026
@nodejs-github-bot

This comment was marked as outdated.

@trivikr trivikr added the author ready PRs with CI started, the required approvals, and no outstanding review comments. label Sep 2, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

codebytere added a commit to codebytere/node that referenced this pull request Sep 7, 2026
The registry behind `AddEnvironmentCleanupHook()` is keyed on
{isolate, fun, arg} and asserts that every insertion is unique. Two
Environments on one isolate that register the same hook, which the
Node-API documentation allows per environment, abort the process on
the second `napi_add_env_cleanup_hook()`.

Key the registry on `arg` only and tell entries apart by Environment:
adding the same hook to one Environment twice still aborts as
documented, and removal prefers the current Environment's registration,
falling back to a matching one from another Environment when there is
no current context. Because the entry to remove after a hook has run can
no longer be found by {isolate, fun, arg} alone, `CleanupHookThunkRun()`
marks its entry as running and erases exactly that entry afterwards; a
removal of a running entry (a hook removing itself, as `~ObjectWrap()`
does) is a no-op, which keeps the use-after-free fixed by nodejs#65630 fixed.

Refs: nodejs#63985
Refs: nodejs#65630
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
everett1992 added a commit to everett1992/node that referenced this pull request Sep 10, 2026
CleanupHookThunkRun() read thunk->isolate/fun/arg from the
CleanupHookThunk after invoking thunk->fun(). For every
node::ObjectWrap alive at teardown, thunk->fun is
ObjectWrap::CleanupHook, which deletes the wrap; ~ObjectWrap() calls
RemoveEnvironmentCleanupHook() itself, erasing the CleanupHookThunk
from the registry and freeing the node it lives in. The subsequent
read of thunk->isolate/fun/arg to make the (now redundant) second
RemoveEnvironmentCleanupHook() call was therefore a use-after-free.

Cache the fields before running the hook so nothing is read from
`thunk` once it may have been freed.

Taken over from nodejs#65196, which has been inactive; the original change is
unmodified apart from the added comment.

This also unblocks nodejs#65042, the backport of the cleanup hook registry to
v24.x. Without that registry ~ObjectWrap() asserts during garbage
collection, so every 24.x runtime aborts for ObjectWrap addons
(nodejs#65446), as do 26.x runtimes before 26.4.0 when used with newer
headers (nodejs#65262).

Fixes: nodejs#65195
Refs: nodejs#65196
Refs: nodejs#65042
Refs: nodejs#65446
Refs: nodejs#65262
Assisted-by: a closed-source coding agent
Co-authored-by: Sreehari Annam <sreehari.annam@gmail.com>
Signed-off-by: Caleb Everett <everett.caleb@gmail.com>
PR-URL: nodejs#65630
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: Shelley Vohr <shelley.vohr@gmail.com>
codebytere added a commit to codebytere/node that referenced this pull request Sep 11, 2026
The registry behind `AddEnvironmentCleanupHook()` is keyed on
{isolate, fun, arg} and asserts that every insertion is unique. Two
Environments on one isolate that register the same hook, which the
Node-API documentation allows per environment, abort the process on
the second `napi_add_env_cleanup_hook()`.

Key the registry on `arg` only and tell entries apart by Environment:
adding the same hook to one Environment twice still aborts as
documented, and removal prefers the current Environment's registration,
falling back to a matching one from another Environment when there is
no current context. Because the entry to remove after a hook has run can
no longer be found by {isolate, fun, arg} alone, `CleanupHookThunkRun()`
marks its entry as running and erases exactly that entry afterwards; a
removal of a running entry (a hook removing itself, as `~ObjectWrap()`
does) is a no-op, which keeps the use-after-free fixed by nodejs#65630 fixed.

Refs: nodejs#63985
Refs: nodejs#65630
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
codebytere added a commit to codebytere/node that referenced this pull request Sep 11, 2026
The registry behind `AddEnvironmentCleanupHook()` is keyed on
{isolate, fun, arg} and asserts that every insertion is unique. Two
Environments on one isolate that register the same hook, which the
Node-API documentation allows per environment, abort the process on
the second `napi_add_env_cleanup_hook()`.

Key the registry on `arg` only and tell entries apart by Environment:
adding the same hook to one Environment twice still aborts as
documented, and removal prefers the current Environment's registration,
falling back to a matching one from another Environment when there is
no current context. Because the entry to remove after a hook has run can
no longer be found by {isolate, fun, arg} alone, `CleanupHookThunkRun()`
marks its entry as running and erases exactly that entry afterwards; a
removal of a running entry (a hook removing itself, as `~ObjectWrap()`
does) is a no-op, which keeps the use-after-free fixed by nodejs#65630 fixed.

Refs: nodejs#63985
Refs: nodejs#65630
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
codebytere added a commit to codebytere/node that referenced this pull request Sep 13, 2026
The registry behind `AddEnvironmentCleanupHook()` is keyed on
{isolate, fun, arg} and asserts that every insertion is unique. Two
Environments on one isolate that register the same hook, which the
Node-API documentation allows per environment, abort the process on
the second `napi_add_env_cleanup_hook()`.

Key the registry on `arg` only and tell entries apart by Environment:
adding the same hook to one Environment twice still aborts as
documented, and removal prefers the current Environment's registration,
falling back to a matching one from another Environment when there is
no current context. Because the entry to remove after a hook has run can
no longer be found by {isolate, fun, arg} alone, `CleanupHookThunkRun()`
marks its entry as running and erases exactly that entry afterwards; a
removal of a running entry (a hook removing itself, as `~ObjectWrap()`
does) is a no-op, which keeps the use-after-free fixed by nodejs#65630 fixed.

Refs: nodejs#63985
Refs: nodejs#65630
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
aduh95 pushed a commit that referenced this pull request Sep 15, 2026
CleanupHookThunkRun() read thunk->isolate/fun/arg from the
CleanupHookThunk after invoking thunk->fun(). For every
node::ObjectWrap alive at teardown, thunk->fun is
ObjectWrap::CleanupHook, which deletes the wrap; ~ObjectWrap() calls
RemoveEnvironmentCleanupHook() itself, erasing the CleanupHookThunk
from the registry and freeing the node it lives in. The subsequent
read of thunk->isolate/fun/arg to make the (now redundant) second
RemoveEnvironmentCleanupHook() call was therefore a use-after-free.

Cache the fields before running the hook so nothing is read from
`thunk` once it may have been freed.

Taken over from #65196, which has been inactive; the original change is
unmodified apart from the added comment.

This also unblocks #65042, the backport of the cleanup hook registry to
v24.x. Without that registry ~ObjectWrap() asserts during garbage
collection, so every 24.x runtime aborts for ObjectWrap addons
(#65446), as do 26.x runtimes before 26.4.0 when used with newer
headers (#65262).

Fixes: #65195
Refs: #65196
Refs: #65042
Refs: #65446
Refs: #65262
Assisted-by: a closed-source coding agent
Co-authored-by: Sreehari Annam <sreehari.annam@gmail.com>
Signed-off-by: Caleb Everett <everett.caleb@gmail.com>
PR-URL: #65630
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: Shelley Vohr <shelley.vohr@gmail.com>
MarshallOfSound pushed a commit to electron/electron that referenced this pull request Sep 16, 2026
* chore: squash the perfetto backports into one Node.js patch

The perfetto trace agent came in as six separate cherry-picks of
nodejs/node#64565 and nodejs/node#64721. Both are marked dont-land-on-v24.x
upstream, so they stay for as long as Electron ships Node.js 24; carry them
as one patch. Likewise fold the CleanupHookThunkRun regression test into
the patch for its fix, matching nodejs/node#65630.

No change to the patched tree.

* chore: keep the test commit's trailers on the folded cleanup-hook patch
codebytere added a commit to codebytere/node that referenced this pull request Sep 19, 2026
The registry behind `AddEnvironmentCleanupHook()` is keyed on
{isolate, fun, arg} and asserts that every insertion is unique. Two
Environments on one isolate that register the same hook, which the
Node-API documentation allows per environment, abort the process on
the second `napi_add_env_cleanup_hook()`.

Key the registry on `arg` only and tell entries apart by Environment:
adding the same hook to one Environment twice still aborts as
documented, and removal prefers the current Environment's registration,
falling back to a matching one from another Environment when there is
no current context. Because the entry to remove after a hook has run can
no longer be found by {isolate, fun, arg} alone, `CleanupHookThunkRun()`
marks its entry as running and erases exactly that entry afterwards; a
removal of a running entry (a hook removing itself, as `~ObjectWrap()`
does) is a no-op, which keeps the use-after-free fixed by nodejs#65630 fixed.

Refs: nodejs#63985
Refs: nodejs#65630
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
panva pushed a commit to panva/node that referenced this pull request Sep 20, 2026
CleanupHookThunkRun() read thunk->isolate/fun/arg from the
CleanupHookThunk after invoking thunk->fun(). For every
node::ObjectWrap alive at teardown, thunk->fun is
ObjectWrap::CleanupHook, which deletes the wrap; ~ObjectWrap() calls
RemoveEnvironmentCleanupHook() itself, erasing the CleanupHookThunk
from the registry and freeing the node it lives in. The subsequent
read of thunk->isolate/fun/arg to make the (now redundant) second
RemoveEnvironmentCleanupHook() call was therefore a use-after-free.

Cache the fields before running the hook so nothing is read from
`thunk` once it may have been freed.

Taken over from nodejs#65196, which has been inactive; the original change is
unmodified apart from the added comment.

This also unblocks nodejs#65042, the backport of the cleanup hook registry to
v24.x. Without that registry ~ObjectWrap() asserts during garbage
collection, so every 24.x runtime aborts for ObjectWrap addons
(nodejs#65446), as do 26.x runtimes before 26.4.0 when used with newer
headers (nodejs#65262).

Fixes: nodejs#65195
Refs: nodejs#65196
Refs: nodejs#65042
Refs: nodejs#65446
Refs: nodejs#65262
Co-authored-by: Sreehari Annam <sreehari.annam@gmail.com>
Signed-off-by: Caleb Everett <everett.caleb@gmail.com>
PR-URL: nodejs#65630
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: Shelley Vohr <shelley.vohr@gmail.com>
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
@panva panva added the backport-open-v24.x Indicate that the PR has an open backport label Sep 20, 2026
nodejs-github-bot pushed a commit that referenced this pull request Sep 20, 2026
The registry behind `AddEnvironmentCleanupHook()` is keyed on
{isolate, fun, arg} and asserts that every insertion is unique. Two
Environments on one isolate that register the same hook, which the
Node-API documentation allows per environment, abort the process on
the second `napi_add_env_cleanup_hook()`.

Key the registry on `arg` only and tell entries apart by Environment:
adding the same hook to one Environment twice still aborts as
documented, and removal prefers the current Environment's registration,
falling back to a matching one from another Environment when there is
no current context. Because the entry to remove after a hook has run can
no longer be found by {isolate, fun, arg} alone, `CleanupHookThunkRun()`
marks its entry as running and erases exactly that entry afterwards; a
removal of a running entry (a hook removing itself, as `~ObjectWrap()`
does) is a no-op, which keeps the use-after-free fixed by #65630 fixed.

Refs: #63985
Refs: #65630
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65777
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
aduh95 pushed a commit that referenced this pull request Sep 21, 2026
CleanupHookThunkRun() read thunk->isolate/fun/arg from the
CleanupHookThunk after invoking thunk->fun(). For every
node::ObjectWrap alive at teardown, thunk->fun is
ObjectWrap::CleanupHook, which deletes the wrap; ~ObjectWrap() calls
RemoveEnvironmentCleanupHook() itself, erasing the CleanupHookThunk
from the registry and freeing the node it lives in. The subsequent
read of thunk->isolate/fun/arg to make the (now redundant) second
RemoveEnvironmentCleanupHook() call was therefore a use-after-free.

Cache the fields before running the hook so nothing is read from
`thunk` once it may have been freed.

Taken over from #65196, which has been inactive; the original change is
unmodified apart from the added comment.

This also unblocks #65042, the backport of the cleanup hook registry to
v24.x. Without that registry ~ObjectWrap() asserts during garbage
collection, so every 24.x runtime aborts for ObjectWrap addons
(#65446), as do 26.x runtimes before 26.4.0 when used with newer
headers (#65262).

Fixes: #65195
Refs: #65196
Refs: #65042
Refs: #65446
Refs: #65262
Co-authored-by: Sreehari Annam <sreehari.annam@gmail.com>
Signed-off-by: Caleb Everett <everett.caleb@gmail.com>
PR-URL: #65630
Backport-PR-URL: #66128
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
@panva panva added backported-to-v24.x PRs backported to the v24.x-staging branch. and removed backport-open-v24.x Indicate that the PR has an open backport labels Sep 21, 2026
aduh95 pushed a commit that referenced this pull request Sep 28, 2026
The registry behind `AddEnvironmentCleanupHook()` is keyed on
{isolate, fun, arg} and asserts that every insertion is unique. Two
Environments on one isolate that register the same hook, which the
Node-API documentation allows per environment, abort the process on
the second `napi_add_env_cleanup_hook()`.

Key the registry on `arg` only and tell entries apart by Environment:
adding the same hook to one Environment twice still aborts as
documented, and removal prefers the current Environment's registration,
falling back to a matching one from another Environment when there is
no current context. Because the entry to remove after a hook has run can
no longer be found by {isolate, fun, arg} alone, `CleanupHookThunkRun()`
marks its entry as running and erases exactly that entry afterwards; a
removal of a running entry (a hook removing itself, as `~ObjectWrap()`
does) is a no-op, which keeps the use-after-free fixed by #65630 fixed.

Refs: #63985
Refs: #65630
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65777
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
aduh95 pushed a commit that referenced this pull request Sep 28, 2026
The registry behind `AddEnvironmentCleanupHook()` is keyed on
{isolate, fun, arg} and asserts that every insertion is unique. Two
Environments on one isolate that register the same hook, which the
Node-API documentation allows per environment, abort the process on
the second `napi_add_env_cleanup_hook()`.

Key the registry on `arg` only and tell entries apart by Environment:
adding the same hook to one Environment twice still aborts as
documented, and removal prefers the current Environment's registration,
falling back to a matching one from another Environment when there is
no current context. Because the entry to remove after a hook has run can
no longer be found by {isolate, fun, arg} alone, `CleanupHookThunkRun()`
marks its entry as running and erases exactly that entry afterwards; a
removal of a running entry (a hook removing itself, as `~ObjectWrap()`
does) is a no-op, which keeps the use-after-free fixed by #65630 fixed.

Refs: #63985
Refs: #65630
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65777
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
MarshallOfSound added a commit to electron/electron that referenced this pull request Oct 15, 2026
* chore: bump node in DEPS to v24.22.0

* fix(patch): enable the V8 sandbox in shared-cage builds

Upstream now enables the V8 sandbox only for shared-cage builds. Keep
Electron's behaviour of enabling it whenever pointer compression is on,
matching Chromium's V8. The unofficial.gni x86 hunk landed upstream.

Ref: nodejs/node#62237
Ref: nodejs/node#65491

* fix(patch): use names for asymmetric key algorithms

Upstream dropped the BoringSSL guard in DhKeyGenTraits::Setup(), removed
ReadASN1Element() from ncrypto, and no longer includes <openssl/evp.h>
from env.h, so those hunks are no longer needed.

Ref: nodejs/node#65966
Ref: nodejs/node#66108
Ref: nodejs/node#65484

* fix(patch): skip RSA-PSS keygen deprecation tests with BoringSSL

The skip is now upstream.

Ref: nodejs/node#65808

* fix(patch): detach cppgc wrappers from their Realm before it is freed

Upstream's CppgcWrapperList::Cleanup() now pops nodes itself and the
wrapper owns and frees its list node, so the local PopFront/delete hunk
is dropped.

Ref: nodejs/node#65778

* fix(patch): gate jspi flag on V8_ENABLE_WEBASSEMBLY

Ref: nodejs/node#66082

* chore: remove upstreamed patches

- nodejs/node#63985
- nodejs/node#65630
- nodejs/node#64453
- nodejs/node#65778

* chore: update patches (trivial only)

* chore: update filenames.auto.gni

* fix(patch): discover hashes from OpenSSL providers

ncrypto's DigestCache::aliases() now has a function-local static, so
ncrypto needs the no_exit_time_destructors config too.

Ref: nodejs/node#65484

* fix(patch): read the peer certificate chain without consuming it

sk_X509_num() returns size_t in BoringSSL.

Ref: nodejs/node#65602

* fix(patch): fix shared buffer growability validation

Ref: nodejs/node#65845

* chore: update patches (trivial only)

* node#65820: src: fix null pointer call when running without a startup snapshot

SnapshotData::AsEmbedderWrapper() was removed. Both call sites wrap the
embedded snapshot, so use EmbedderSnapshotData::BuiltinSnapshotData().

Ref: nodejs/node#65820

* node#65407: src: apply IsolateSettings when using a snapshot

node::CreateEnvironment() no longer installs the per-isolate message
listener when it builds the environment from a snapshot, so stop
compensating for that and let SetIsolateUpForNode() add it.

Ref: nodejs/node#65407

* test: drop removed wasm rab integration flag from webidl buffer source test

Chromium's V8 removed --experimental-wasm-rab-integration after shipping
it; the new test-internal-webidl-buffer-source.js passes it.

Ref: nodejs/node#65845
Ref: https://chromium-review.googlesource.com/c/v8/v8/+/7708297

* chore: remove obsolete TLS external-memory accounting patch

Upstream moved the SecureContext external-memory Increase() into Init()
and guards the Decrease() on ctx_, so the leak this patch fixed is gone.
The patch's removal of that guard now double-decrements when v24.22.0's
TLSSocket closes a single-use context before the wrapper is destroyed,
tripping V8's ExternalMemoryAccounter DCHECK in every TLS test. Already
dropped on main in #53524.

* test: skip new rmSync permission error test when the Node suite runs as root

test-fs-rmSync-special-char-additional-error.js is new in v24.22.0 and
relies on a read-only directory blocking removal, which root bypasses;
the Linux Node.js test job runs as root.

Ref: nodejs/node#64453

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: Sam Attard <sattard@anthropic.com>
MarshallOfSound added a commit to electron/electron that referenced this pull request Oct 15, 2026
* chore: bump node in DEPS to v24.22.0

* fix(patch): enable the V8 sandbox in shared-cage builds

Upstream now enables the V8 sandbox only for shared-cage builds. Keep
Electron's behaviour of enabling it whenever pointer compression is on,
matching Chromium's V8. The unofficial.gni x86 hunk landed upstream.

Ref: nodejs/node#62237
Ref: nodejs/node#65491

* fix(patch): use names for asymmetric key algorithms

Upstream dropped the BoringSSL guard in DhKeyGenTraits::Setup(), removed
ReadASN1Element() from ncrypto, and no longer includes <openssl/evp.h>
from env.h, so those hunks are no longer needed.

Ref: nodejs/node#65966
Ref: nodejs/node#66108
Ref: nodejs/node#65484

* fix(patch): skip RSA-PSS keygen deprecation tests with BoringSSL

The skip is now upstream.

Ref: nodejs/node#65808

* fix(patch): detach cppgc wrappers from their Realm before it is freed

Upstream's CppgcWrapperList::Cleanup() now pops nodes itself and the
wrapper owns and frees its list node, so the local PopFront/delete hunk
is dropped.

Ref: nodejs/node#65778

* fix(patch): gate jspi flag on V8_ENABLE_WEBASSEMBLY

Ref: nodejs/node#66082

* chore: remove upstreamed patches

- nodejs/node#63985
- nodejs/node#65630
- nodejs/node#64453
- nodejs/node#65778

* chore: update patches (trivial only)

* chore: update filenames.auto.gni

* fix(patch): discover hashes from OpenSSL providers

ncrypto's DigestCache::aliases() now has a function-local static, so
ncrypto needs the no_exit_time_destructors config too.

Ref: nodejs/node#65484

* fix(patch): read the peer certificate chain without consuming it

sk_X509_num() returns size_t in BoringSSL.

Ref: nodejs/node#65602

* fix(patch): fix shared buffer growability validation

Ref: nodejs/node#65845

* chore: update patches (trivial only)

* node#65820: src: fix null pointer call when running without a startup snapshot

SnapshotData::AsEmbedderWrapper() was removed. Both call sites wrap the
embedded snapshot, so use EmbedderSnapshotData::BuiltinSnapshotData().

Ref: nodejs/node#65820

* node#65407: src: apply IsolateSettings when using a snapshot

node::CreateEnvironment() no longer installs the per-isolate message
listener when it builds the environment from a snapshot, so stop
compensating for that and let SetIsolateUpForNode() add it.

Ref: nodejs/node#65407

* test: drop removed wasm rab integration flag from webidl buffer source test

Chromium's V8 removed --experimental-wasm-rab-integration after shipping
it; the new test-internal-webidl-buffer-source.js passes it.

Ref: nodejs/node#65845
Ref: https://chromium-review.googlesource.com/c/v8/v8/+/7708297

* test: skip new rmSync permission error test when the Node suite runs as root

test-fs-rmSync-special-char-additional-error.js is new in v24.22.0 and
relies on a read-only directory blocking removal, which root bypasses;
the Linux Node.js test job runs as root.

Ref: nodejs/node#64453

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: Sam Attard <sattard@anthropic.com>
MarshallOfSound added a commit to electron/electron that referenced this pull request Oct 15, 2026
* chore: bump node in DEPS to v24.22.0

* fix(patch): enable the V8 sandbox in shared-cage builds

Upstream now enables the V8 sandbox only for shared-cage builds. Keep
Electron's behaviour of enabling it whenever pointer compression is on,
matching Chromium's V8. The unofficial.gni x86 hunk landed upstream.

Ref: nodejs/node#62237
Ref: nodejs/node#65491

* fix(patch): use names for asymmetric key algorithms

Upstream dropped the BoringSSL guard in DhKeyGenTraits::Setup(), removed
ReadASN1Element() from ncrypto, and no longer includes <openssl/evp.h>
from env.h, so those hunks are no longer needed.

Ref: nodejs/node#65966
Ref: nodejs/node#66108
Ref: nodejs/node#65484

* fix(patch): skip RSA-PSS keygen deprecation tests with BoringSSL

The skip is now upstream.

Ref: nodejs/node#65808

* fix(patch): detach cppgc wrappers from their Realm before it is freed

Upstream's CppgcWrapperList::Cleanup() now pops nodes itself and the
wrapper owns and frees its list node, so the local PopFront/delete hunk
is dropped.

Ref: nodejs/node#65778

* fix(patch): gate jspi flag on V8_ENABLE_WEBASSEMBLY

Ref: nodejs/node#66082

* chore: remove upstreamed patches

- nodejs/node#63985
- nodejs/node#65630
- nodejs/node#64453
- nodejs/node#65778

* chore: update patches (trivial only)

* chore: update filenames.auto.gni

* fix(patch): discover hashes from OpenSSL providers

ncrypto's DigestCache::aliases() now has a function-local static, so
ncrypto needs the no_exit_time_destructors config too.

Ref: nodejs/node#65484

* fix(patch): read the peer certificate chain without consuming it

sk_X509_num() returns size_t in BoringSSL.

Ref: nodejs/node#65602

* chore: update patches (trivial only)

* node#65820: src: fix null pointer call when running without a startup snapshot

SnapshotData::AsEmbedderWrapper() was removed. Both call sites wrap the
embedded snapshot, so use EmbedderSnapshotData::BuiltinSnapshotData().

Ref: nodejs/node#65820

* node#65407: src: apply IsolateSettings when using a snapshot

node::CreateEnvironment() no longer installs the per-isolate message
listener when it builds the environment from a snapshot, so stop
compensating for that and let SetIsolateUpForNode() add it.

Ref: nodejs/node#65407

* test: drop removed wasm rab integration flag from webidl buffer source test

Chromium's V8 removed --experimental-wasm-rab-integration after shipping
it; the new test-internal-webidl-buffer-source.js passes it.

Ref: nodejs/node#65845
Ref: https://chromium-review.googlesource.com/c/v8/v8/+/7708297

* chore: remove obsolete TLS external-memory accounting patch

Upstream moved the SecureContext external-memory Increase() into Init()
and guards the Decrease() on ctx_, so the leak this patch fixed is gone.
The patch's removal of that guard now double-decrements when v24.22.0's
TLSSocket closes a single-use context before the wrapper is destroyed,
tripping V8's ExternalMemoryAccounter DCHECK in every TLS test. Already
dropped on main in #53524.

* test: skip new rmSync permission error test when the Node suite runs as root

test-fs-rmSync-special-char-additional-error.js is new in v24.22.0 and
relies on a read-only directory blocking removal, which root bypasses;
the Linux Node.js test job runs as root.

Ref: nodejs/node#64453

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: Sam Attard <sattard@anthropic.com>
MarshallOfSound added a commit to electron/electron that referenced this pull request Oct 15, 2026
* chore: bump node in DEPS to v24.22.0

* fix(patch): enable the V8 sandbox in shared-cage builds

Upstream now enables the V8 sandbox only for shared-cage builds. Keep
Electron's behaviour of enabling it whenever pointer compression is on,
matching Chromium's V8. The unofficial.gni x86 hunk landed upstream.

Ref: nodejs/node#62237
Ref: nodejs/node#65491

* fix(patch): use names for asymmetric key algorithms

Upstream dropped the BoringSSL guard in DhKeyGenTraits::Setup(), removed
ReadASN1Element() from ncrypto, and no longer includes <openssl/evp.h>
from env.h, so those hunks are no longer needed.

Ref: nodejs/node#65966
Ref: nodejs/node#66108
Ref: nodejs/node#65484

* fix(patch): skip RSA-PSS keygen deprecation tests with BoringSSL

The skip is now upstream.

Ref: nodejs/node#65808

* fix(patch): detach cppgc wrappers from their Realm before it is freed

Upstream's CppgcWrapperList::Cleanup() now pops nodes itself and the
wrapper owns and frees its list node, so the local PopFront/delete hunk
is dropped.

Ref: nodejs/node#65778

* fix(patch): gate jspi flag on V8_ENABLE_WEBASSEMBLY

Ref: nodejs/node#66082

* chore: remove upstreamed patches

- nodejs/node#63985
- nodejs/node#65630
- nodejs/node#64453
- nodejs/node#65778

* chore: update patches (trivial only)

* chore: update filenames.auto.gni

* fix(patch): discover hashes from OpenSSL providers

ncrypto's DigestCache::aliases() now has a function-local static, so
ncrypto needs the no_exit_time_destructors config too.

Ref: nodejs/node#65484

* fix(patch): read the peer certificate chain without consuming it

sk_X509_num() returns size_t in BoringSSL.

Ref: nodejs/node#65602

* fix(patch): fix shared buffer growability validation

Ref: nodejs/node#65845

* chore: update patches (trivial only)

* node#65820: src: fix null pointer call when running without a startup snapshot

SnapshotData::AsEmbedderWrapper() was removed. Both call sites wrap the
embedded snapshot, so use EmbedderSnapshotData::BuiltinSnapshotData().

Ref: nodejs/node#65820

* node#65407: src: apply IsolateSettings when using a snapshot

node::CreateEnvironment() no longer installs the per-isolate message
listener when it builds the environment from a snapshot, so stop
compensating for that and let SetIsolateUpForNode() add it.

Ref: nodejs/node#65407

* test: drop removed wasm rab integration flag from webidl buffer source test

Chromium's V8 removed --experimental-wasm-rab-integration after shipping
it; the new test-internal-webidl-buffer-source.js passes it.

Ref: nodejs/node#65845
Ref: https://chromium-review.googlesource.com/c/v8/v8/+/7708297

* test: skip new rmSync permission error test when the Node suite runs as root

test-fs-rmSync-special-char-additional-error.js is new in v24.22.0 and
relies on a read-only directory blocking removal, which root bypasses;
the Linux Node.js test job runs as root.

Ref: nodejs/node#64453

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: Sam Attard <sattard@anthropic.com>
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. backported-to-v24.x PRs backported to the v24.x-staging branch. c++ Issues and PRs that require attention from people who are familiar with C++. commit-queue-squash PRs the Commit Queue should land as one squashed commit. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use-after-free in CleanupHookThunkRun for every node::ObjectWrap alive at teardown

5 participants