-
Notifications
You must be signed in to change notification settings - Fork 465
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
[v2.x] Backport ObjectWrap reference-related changes #723
[v2.x] Backport ObjectWrap reference-related changes #723
Conversation
Re #646 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
@addaleax I actually had to add another commit, also cleanly applied – the one that updates the BigInt test for changes to the expected error message in core – since we're testing 2.x again. |
@addaleax this crashes on Node.js v8.x because we didn't backport the reference tracking changes before it went EOL. |
@gabrielschulhof Sigh … yeah, not sure what to do about that. I don’t suppose we want to detect the Node.js version in here and skip the relevant fixes for Node < 10? I would be okay with that but I’d also understand that it’s not a great solution. (I mean, it’s also not clear that N-API had breaking changes in core. But that’s not really something we can do anything about…) |
@addaleax the detection would have to happen at runtime because anything built against Node.js 8.x must also run without recompilation on later versions. |
@gabrielschulhof Yes, I was talking about |
@addaleax I started implementing it but then I realized that we cannot just call |
I don’t quite see how it does that? It’s not like the Node.js version is going to vary between contexts, or is that a concern here? |
@addaleax no, but it should at least be assigned in a thread-safe fashion. |
@gabrielschulhof Do we assume C++11 here? It would come with |
@addaleax I think it's safe to assume C++11. |
@@ -10,6 +10,7 @@ | |||
// Note: Do not include this file directly! Include "napi.h" instead. | |||
|
|||
#include <algorithm> | |||
#include <atomic> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a new change, not part of any cherry-pick.
extern std::atomic_bool needs_objectwrap_destructor_fix; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a new change, not part of any cherry-pick.
namespace Napi { \ | ||
namespace details { \ | ||
std::atomic_bool needs_objectwrap_destructor_fix(false); \ | ||
} \ | ||
} \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a new change, not part of any cherry-pick.
const napi_node_version* nver = Napi::VersionManagement::GetNodeVersion(env); | ||
Napi::details::needs_objectwrap_destructor_fix = | ||
(nver->major < 10 || | ||
(nver->major == 10 && nver->minor < 15) || | ||
(nver->major == 10 && nver->minor == 15 && nver->patch < 3)); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a new change, not part of any cherry-pick
if (Napi::details::needs_objectwrap_destructor_fix) { | ||
// If construction failed we delete the reference via | ||
// `napi_remove_wrap()`, not via `napi_delete_reference()` in the | ||
// `Reference<Object>` destructor. This will prevent the | ||
// `Reference<Object>` destructor from doing a double delete of this | ||
// reference. | ||
_ref = nullptr; | ||
_env = nullptr; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a new change, not part of any cherry-pick.
@addaleax PTAL. I pointed out the changes introduced by the commit that is not a cherry-pick. |
Oh, but wait! 2.x doesn't support Node.js 14. |
@mhdawson I think we're good to land this PR and make a final 2.x release. PTAL! |
Ensure that no native instance pointer is associated with the JavaScript object under construction if the native constructor causes a JavaScript exception to be thrown. Two different cases must be taken into consideration: 1. The exception in the constructor was caused by `ObjectWrap<T>::ObjectWrap` when the call to `napi_wrap()` failed. 2. The exception in the constructor was caused by the constructor of the subclass of `ObjectWrap<T>` after `napi_wrap()` was already successful. Fixes: nodejs#599 Co-authored-by: blagoev <lubo@blagoev.com> PR-URL: nodejs#600 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Currently, when the `ObjectWrap` constructor runs, it calls `napi_wrap()`, adding a finalize callback to the freshly created JS object. However, if the `ObjectWrap` instance is prematurely deleted, for example because a subclass constructor throws – which seems like a reasonable scenario – that finalize callback was not removed, possibly leading to a use-after-free crash. This commit adds a call `napi_remove_wrap()` from the `ObjectWrap` destructor, and a test for that scenario. This also changes the code to use the correct pointer type in `FinalizeCallback`, which may not match the incorretct one in cases of multiple inheritance. Fixes: node-ffi-napi/weak-napi#16 PR-URL: nodejs#475 Reviewed-By: Hitesh Kanwathirtha <digitalinfinity@gmail.com> Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Co-authored-by: Gabriel Schulhof <gabriel.schulhof@intel.com>
`napi_remove_wrap()` was intended for objects that are alive for which the native addon wishes to withdraw its native pointer, and perhaps replace it with another. Therefore we need not `napi_remove_wrap()` during gc/env-cleanup. It is sufficient to `napi_delete_reference()`, as `Reference<Object>` already does. We need only `napi_remove_wrap()` if the construction failed and therefore no gc callback will ever happen. This change also removes references to `ObjectWrapConstructionContext` from the header because the class is not used anymore. Fixes: nodejs#660 Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Kevin Eady <kevin.c.eady@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
nodejs/node@689ab46 changed the expected error for one of the BigInt test cases. This is ok because BigInt is still in experimental. However, the result was a failed/hanging test. See nodejs/build#2131 This changes the test to accept either the new or old behaviour. We need that so that older versions of Node.js will also pass the test until the the node core commit above is backported. PR-URL: nodejs#649 Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com> Reviewed-By: Nicola Del Gobbo <nicoladelgobbo@gmail.com>
On Node.js versions earlier than 10.15.3 references were being cleaned up differently. This has resulted in a segfault because of a double free when the `ObjectWrap<T>` subclass constructor throws. We need a fix that only runs on Node.js < 10.15.3. We use `Napi::VersionManagement::GetNodeVersion()` at addon load time to determine whether we need the workaround. We store the result in an atomic boolean and consult its value whenever we destroy an `ObjectWrap<T>` instance.
31be779
to
470f130
Compare
Rebased because of the security release. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
No description provided.