Skip to content

Test cleanup hook uaf regression 65196 - #65630

Draft
everett1992 wants to merge 2 commits into
nodejs:mainfrom
everett1992:test-cleanup-hook-uaf-regression-65196
Draft

Test cleanup hook uaf regression 65196#65630
everett1992 wants to merge 2 commits into
nodejs:mainfrom
everett1992:test-cleanup-hook-uaf-regression-65196

Conversation

@everett1992

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 v29.19.0

everett1992 and others added 2 commits August 28, 2026 21:09
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>
@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
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++. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants