Conversation
4c7f8ba to
f4bfde2
Compare
| inline ObjectWrap<T>::~ObjectWrap() { | ||
| if (!IsEmpty()) { | ||
| Object object = Value(); | ||
| if (!object.IsEmpty()) |
There was a problem hiding this comment.
This code could probably benefit from a comment
- Removed NodeJS v10.x.x support Waiting for nodejs/node-addon-api#475 to implement the provided solution and to bump a new version of the module `node-addon-api`
|
Looks like this may cause issues in Node.js versions that don’t have nodejs/node#24494 … I’ll try and see if there’s some way to detect/work around that. |
mhdawson
left a comment
There was a problem hiding this comment.
LGTM assuming we get it passing on all LTS versions.
|
Added |
|
Hey guys, funny enough I encountered the issue anticipated right here. When I throw |
|
|
||
| Reference<Object>* instanceRef = instance; | ||
| Reference<Object>* instanceRef = this; | ||
| *instanceRef = Reference<Object>(env, ref); |
There was a problem hiding this comment.
These two statements though work, as intended but seem to be a little bit misleading. Having a fast peek over the code you could think that we re-move-assign the whole *this object.
I suggest making an explicit call to parent move-assignment operator, namely:
Reference::operator=({env, ref});It is more laconic and clear from my point of view.
| template <typename T> | ||
| inline void ObjectWrap<T>::FinalizeCallback(napi_env /*env*/, void* data, void* /*hint*/) { | ||
| T* instance = reinterpret_cast<T*>(data); | ||
| ObjectWrap<T>* instance = static_cast<ObjectWrap<T>*>(data); |
There was a problem hiding this comment.
I propose using the auto keyword here in order to decrease verbosity. Or you could simply rename argument data to instance and do it in one statement:
inline void ObjectWrap<T>::FinalizeCallback(napi_env /*env*/, void * instance, void * /*hint*/) {
delete static_cast<ObjectWrap<T>*>(instance);
}Any of two options are alright from my viewpoint, though it may just be a matter of style.
|
Also seeing this issue (double call to FinalizeCallback due to exception in subclass constructor), and this PR fixed the SEGV. |
Fix crashes when the ctor of an ObjectWrap subclass throws. See nodejs#475
|
We addressed the portion whereby an exception in the |
|
@gabrielschulhof I’ll look into rebasing this 👍 |
0fd142b to
c243f71
Compare
|
@gabrielschulhof I’ve rebased this, but I don’t quite understand #600 and the added complexity there – the destructor should undo what the constructor did in case that there’s an exception, and I don’t really see why that’s enough. This also doesn’t pass tests yet. |
|
@addaleax I think you're right. |
|
Sorry, wrong button 😳 |
diff --git a/napi-inl.h b/napi-inl.h
index fde0a66..2855e80 100644
--- a/napi-inl.h
+++ b/napi-inl.h
@@ -2702,37 +2702,6 @@ inline Object FunctionReference::New(const std::vector<napi_value>& args) const
// CallbackInfo class
////////////////////////////////////////////////////////////////////////////////
-class ObjectWrapConstructionContext {
- public:
- ObjectWrapConstructionContext(CallbackInfo* info) {
- info->_objectWrapConstructionContext = this;
- }
-
- static inline void SetObjectWrapped(const CallbackInfo& info) {
- if (info._objectWrapConstructionContext == nullptr) {
- Napi::Error::Fatal("ObjectWrapConstructionContext::SetObjectWrapped",
- "_objectWrapConstructionContext is NULL");
- }
- info._objectWrapConstructionContext->_objectWrapped = true;
- }
-
- inline void Cleanup(const CallbackInfo& info) {
- if (_objectWrapped) {
- napi_status status = napi_remove_wrap(info.Env(), info.This(), nullptr);
-
- // There's already a pending exception if we are at this point, so we have
- // no choice but to fatally fail here.
- NAPI_FATAL_IF_FAILED(status,
- "ObjectWrapConstructionContext::Cleanup",
- "Failed to remove wrap from unsuccessfully "
- "constructed ObjectWrap instance");
- }
- }
-
- private:
- bool _objectWrapped = false;
-};
-
inline CallbackInfo::CallbackInfo(napi_env env, napi_callback_info info)
: _env(env), _info(info), _this(nullptr), _dynamicArgs(nullptr), _data(nullptr) {
_argc = _staticArgCount;
@@ -3140,7 +3109,6 @@ inline ObjectWrap<T>::ObjectWrap(const Napi::CallbackInfo& callbackInfo) {
status = napi_wrap(env, wrapper, this, FinalizeCallback, nullptr, &ref);
NAPI_THROW_IF_FAILED_VOID(env, status);
- ObjectWrapConstructionContext::SetObjectWrapped(callbackInfo);
Reference<Object>* instanceRef = this;
*instanceRef = Reference<Object>(env, ref);
}
@@ -3726,23 +3694,15 @@ inline napi_value ObjectWrap<T>::ConstructorCallbackWrapper(
napi_value wrapper = details::WrapCallback([&] {
CallbackInfo callbackInfo(env, info);
- ObjectWrapConstructionContext constructionContext(&callbackInfo);
#ifdef NAPI_CPP_EXCEPTIONS
- try {
- new T(callbackInfo);
- } catch (const Error& e) {
- // Re-throw the error after removing the failed wrap.
- constructionContext.Cleanup(callbackInfo);
- throw e;
- }
+ new T(callbackInfo);
#else
T* instance = new T(callbackInfo);
if (callbackInfo.Env().IsExceptionPending()) {
// We need to clear the exception so that removing the wrap might work.
Error e = callbackInfo.Env().GetAndClearPendingException();
- constructionContext.Cleanup(callbackInfo);
- e.ThrowAsJavaScriptException();
delete instance;
+ e.ThrowAsJavaScriptException();
}
# endif // NAPI_CPP_EXCEPTIONS
return callbackInfo.This();this makes the tests pass by basically reverting most of #600, but with one important caveat: The last Please feel free to use the diff! |
c243f71 to
10a2698
Compare
|
@gabrielschulhof Thanks, done! PTAL |
|
CI:
|
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 Co-authored-by: Gabriel Schulhof <gabriel.schulhof@intel.com>
|
@gabrielschulhof Thanks, done! |
10a2698 to
257393d
Compare
|
CI:
|
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: #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>
|
Landed in 4e88506. |
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>
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>
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/node-addon-api#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>
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/node-addon-api#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>
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/node-addon-api#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>
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/node-addon-api#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>
src: call
napi_remove_wrap()inObjectWrapdtorCurrently, when the
ObjectWrapconstructor runs, it callsnapi_wrap(), adding a finalize callback to the freshly createdJS object.
However, if the
ObjectWrapinstance 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 theObjectWrapdestructor, and a test for that scenario.
Fixes: node-ffi-napi/weak-napi#16
(As you can see, there is a fixup commit attached. I’m not strictly sureSee nodejs/node#27470whether it’s necessary, but just from looking at the code, it seems that
calling
napi_get_reference_value()from the finalize callback is invalid;that seems like a bug in itself, but one that needs to be addressed in
nodejs/node.)
src: make ObjectWrap dtor virtual
Otherwise, subclasses of
ObjectWrapwould not be deletedcorrectly by the
delete instance;line inFinalizeCallback().(This is also just the right thing to do for classes from which
subclasses are supposed to be created.)