Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

src: make realm binding data store weak #47688

Closed
wants to merge 2 commits into from

Conversation

legendecas
Copy link
Member

@legendecas legendecas commented Apr 23, 2023

src: remove aliased buffer weak callback
An AliasedBuffer can be nested in a BaseObject and their weak callbacks
invoke order are not guaranteed. Prevent aliased buffer from being
accessed in its weak callback as its validness can be checked with the
emptiness of the persistent handle.

src: make realm binding data store weak
The binding data must be weak so that it won't keep the realm reachable
from strong GC roots indefinitely. The wrapper object of binding data
should be referenced from JavaScript, thus the binding data should be
reachable throughout the lifetime of the realm.

Fixes #47353

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/realm
  • @nodejs/startup
  • @nodejs/url

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels Apr 23, 2023
@legendecas legendecas added the realm Issues and PRs related to the ShadowRealm API and node::Realm label Apr 23, 2023
@legendecas legendecas marked this pull request as draft April 23, 2023 17:34
// This is necessary to avoid cleaning up base objects before their scheduled
// weak callbacks are invoked, which can lead to accessing to v8 apis during
// the first pass of the weak callback.
realm->env()->SetImmediate([realm](Environment* env) { delete realm; });
Copy link
Member

@joyeecheung joyeecheung Apr 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just remembered that technically shadow realms can outlive the environment...(which is why I added the if (env_ != nullptr) branch in the destructor of shadow realms, I think I ran into that before)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to lead to crashes if the ream has access to any libuv handles within it. The problem is that any handles could emit data and ultimately call into JS.

IMHO the mechanism needs to be more sophisticated and we should track any native resource that the realm can spawn and let the ream be collected only after they have all been disposed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the ideas. Also, I noticed that the ASan build is failing for the URL binding data. I'm going to see how to address the failure too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a cleanup for ShadowRealms when the environment is being freed. This ensures that no shadow realms can outlive the lifetime of the environment. Similar to BaseObjects, no BaseObjects can outlive their creation realm (https://github.com/nodejs/node/blob/main/src/node_realm.cc#L27).

As for the ASan failure of the URL binding data, I've removed the aliased buffer's weak callback as the validness of the aliased buffer can be checked with the emptiness of the persistent handle.

Would you mind taking a look again? Thank you!

@legendecas legendecas force-pushed the shadow-realm/binding-data branch 2 times, most recently from 2f14395 to eadbf52 Compare May 8, 2023 17:32
@legendecas legendecas marked this pull request as ready for review May 8, 2023 17:42
Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@legendecas legendecas added the request-ci Add this label to start a Jenkins CI on a PR. label May 9, 2023
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label May 9, 2023
@nodejs-github-bot
Copy link
Collaborator

src/env.cc Outdated Show resolved Hide resolved
@legendecas
Copy link
Member Author

@joyeecheung would you mind taking a look at this again? Thank you! <3

@@ -915,10 +928,6 @@ Environment::~Environment() {
addon.Close();
}
}

for (auto realm : shadow_realms_) {
realm->OnEnvironmentDestruct();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a test that creates a bunch of shadow realms and then generates a heap snapshot? I think that's how I ran into the shadow realms outliving shadow realms issue and is why this is necessary.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a new test test/pummel/test-heapdump-shadow-realm.js.

@legendecas legendecas added the request-ci Add this label to start a Jenkins CI on a PR. label Jun 8, 2023
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Jun 8, 2023
@nodejs-github-bot
Copy link
Collaborator

An AliasedBuffer can be nested in a BaseObject and their weak callbacks
invoke order are not guaranteed. Prevent aliased buffer from being
accessed in its weak callback as its validness can be checked with the
emptiness of the persistent handle.
The binding data must be weak so that it won't keep the realm reachable
from strong GC roots indefinitely. The wrapper object of binding data
should be referenced from JavaScript, thus the binding data should be
reachable throughout the lifetime of the realm.
@legendecas legendecas added the request-ci Add this label to start a Jenkins CI on a PR. label Jun 13, 2023
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Jun 13, 2023
@legendecas
Copy link
Member Author

Rebased on the tip of the main branch to address CI coverage reports.

@nodejs-github-bot
Copy link
Collaborator

@nodejs-github-bot
Copy link
Collaborator

@legendecas legendecas added commit-queue Add this label to land a pull request using GitHub Actions. commit-queue-rebase Add this label to allow the Commit Queue to land a PR in several commits. labels Jun 14, 2023
@nodejs-github-bot nodejs-github-bot removed the commit-queue Add this label to land a pull request using GitHub Actions. label Jun 14, 2023
@nodejs-github-bot
Copy link
Collaborator

Landed in 8bc6e19...ac0853c

nodejs-github-bot pushed a commit that referenced this pull request Jun 14, 2023
An AliasedBuffer can be nested in a BaseObject and their weak callbacks
invoke order are not guaranteed. Prevent aliased buffer from being
accessed in its weak callback as its validness can be checked with the
emptiness of the persistent handle.

PR-URL: #47688
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
nodejs-github-bot pushed a commit that referenced this pull request Jun 14, 2023
The binding data must be weak so that it won't keep the realm reachable
from strong GC roots indefinitely. The wrapper object of binding data
should be referenced from JavaScript, thus the binding data should be
reachable throughout the lifetime of the realm.

PR-URL: #47688
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
@legendecas legendecas deleted the shadow-realm/binding-data branch June 14, 2023 02:28
RafaelGSS pushed a commit that referenced this pull request Jul 3, 2023
An AliasedBuffer can be nested in a BaseObject and their weak callbacks
invoke order are not guaranteed. Prevent aliased buffer from being
accessed in its weak callback as its validness can be checked with the
emptiness of the persistent handle.

PR-URL: #47688
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
RafaelGSS pushed a commit that referenced this pull request Jul 3, 2023
The binding data must be weak so that it won't keep the realm reachable
from strong GC roots indefinitely. The wrapper object of binding data
should be referenced from JavaScript, thus the binding data should be
reachable throughout the lifetime of the realm.

PR-URL: #47688
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
@RafaelGSS RafaelGSS mentioned this pull request Jul 3, 2023
Ceres6 pushed a commit to Ceres6/node that referenced this pull request Aug 14, 2023
An AliasedBuffer can be nested in a BaseObject and their weak callbacks
invoke order are not guaranteed. Prevent aliased buffer from being
accessed in its weak callback as its validness can be checked with the
emptiness of the persistent handle.

PR-URL: nodejs#47688
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Ceres6 pushed a commit to Ceres6/node that referenced this pull request Aug 14, 2023
The binding data must be weak so that it won't keep the realm reachable
from strong GC roots indefinitely. The wrapper object of binding data
should be referenced from JavaScript, thus the binding data should be
reachable throughout the lifetime of the realm.

PR-URL: nodejs#47688
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Ceres6 pushed a commit to Ceres6/node that referenced this pull request Aug 14, 2023
An AliasedBuffer can be nested in a BaseObject and their weak callbacks
invoke order are not guaranteed. Prevent aliased buffer from being
accessed in its weak callback as its validness can be checked with the
emptiness of the persistent handle.

PR-URL: nodejs#47688
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Ceres6 pushed a commit to Ceres6/node that referenced this pull request Aug 14, 2023
The binding data must be weak so that it won't keep the realm reachable
from strong GC roots indefinitely. The wrapper object of binding data
should be referenced from JavaScript, thus the binding data should be
reachable throughout the lifetime of the realm.

PR-URL: nodejs#47688
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
@ruyadorno ruyadorno added the backport-requested-v18.x PRs awaiting manual backport to the v18.x-staging branch. label Sep 8, 2023
@ruyadorno
Copy link
Member

These commits do not land cleanly on v18.x-staging and will need manual backport in case we want this to land on v18.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport-requested-v18.x PRs awaiting manual backport to the v18.x-staging branch. c++ Issues and PRs that require attention from people who are familiar with C++. commit-queue-rebase Add this label to allow the Commit Queue to land a PR in several commits. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. realm Issues and PRs related to the ShadowRealm API and node::Realm
Projects
None yet
Development

Successfully merging this pull request may close these issues.

memory leak in shadow realms
6 participants