summaryrefslogtreecommitdiff
path: root/src/node_api.cc
diff options
context:
space:
mode:
authorGabriel Schulhof <gabriel.schulhof@intel.com>2017-06-22 14:10:05 +0300
committerMichael Dawson <michael_dawson@ca.ibm.com>2017-07-12 12:05:44 -0400
commitd5b397c9b63507a5dcf6d1da47df2a090d3d73a0 (patch)
treecd63dfde838dd4b7858b1e3a0e6c10da1029f5af /src/node_api.cc
parent3eae3103349b4fd5dc9a5d7a8b50374d6bf43c16 (diff)
downloadandroid-node-v8-d5b397c9b63507a5dcf6d1da47df2a090d3d73a0.tar.gz
android-node-v8-d5b397c9b63507a5dcf6d1da47df2a090d3d73a0.tar.bz2
android-node-v8-d5b397c9b63507a5dcf6d1da47df2a090d3d73a0.zip
n-api: Implement stricter wrapping
Use a stronger criterion to identify objects in the prototype chain that store pointers to native data that were added by previous calls to `napi_wrap()`. Whereas the old criterion for identifying `napi_wrap()`-injected prototype chain objects was to consider an object with an internal field count of 1 to be such an object, the new criterion is to consider an object with an internal field count of 2 such that the second field holds a `v8::External` which itself contains a pointer to a global static string unique to N-API to be a `napi_wrap()`-injected prototype chain object. This greatly reduces the possibility of returning a pointer that was not previously added with `napi_wrap()`, and it allows us to recognize that an object has already undergone `napi_wrap()` and we can thus prevent a chain of wrappers only the first of which is accessible from appearing in the prototype chain, as would be the result of multiple calls to `napi_wrap()` using the same object. PR-URL: https://github.com/nodejs/node/pull/13872 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: Timothy Gu <timothygu99@gmail.com>
Diffstat (limited to 'src/node_api.cc')
-rw-r--r--src/node_api.cc64
1 files changed, 50 insertions, 14 deletions
diff --git a/src/node_api.cc b/src/node_api.cc
index ffccff5b8e..becafc4684 100644
--- a/src/node_api.cc
+++ b/src/node_api.cc
@@ -673,6 +673,38 @@ v8::Local<v8::Object> CreateAccessorCallbackData(napi_env env,
return cbdata;
}
+// Pointer used to identify items wrapped by N-API. Used by FindWrapper and
+// napi_wrap().
+const char napi_wrap_name[] = "N-API Wrapper";
+
+// Search the object's prototype chain for the wrapper object. Usually the
+// wrapper would be the first in the chain, but it is OK for other objects to
+// be inserted in the prototype chain.
+bool FindWrapper(v8::Local<v8::Object> obj,
+ v8::Local<v8::Object>* result = nullptr) {
+ v8::Local<v8::Object> wrapper = obj;
+
+ do {
+ v8::Local<v8::Value> proto = wrapper->GetPrototype();
+ if (proto.IsEmpty() || !proto->IsObject()) {
+ return false;
+ }
+ wrapper = proto.As<v8::Object>();
+ if (wrapper->InternalFieldCount() == 2) {
+ v8::Local<v8::Value> external = wrapper->GetInternalField(1);
+ if (external->IsExternal() &&
+ external.As<v8::External>()->Value() == v8impl::napi_wrap_name) {
+ break;
+ }
+ }
+ } while (true);
+
+ if (result != nullptr) {
+ *result = wrapper;
+ }
+ return true;
+}
+
} // end of namespace v8impl
// Intercepts the Node-V8 module registration callback. Converts parameters
@@ -2046,11 +2078,22 @@ napi_status napi_wrap(napi_env env,
RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg);
v8::Local<v8::Object> obj = value.As<v8::Object>();
- // Create a wrapper object with an internal field to hold the wrapped pointer.
+ // If we've already wrapped this object, we error out.
+ RETURN_STATUS_IF_FALSE(env, !v8impl::FindWrapper(obj), napi_invalid_arg);
+
+ // Create a wrapper object with an internal field to hold the wrapped pointer
+ // and a second internal field to identify the owner as N-API.
v8::Local<v8::ObjectTemplate> wrapper_template;
- ENV_OBJECT_TEMPLATE(env, wrap, wrapper_template, 1);
- v8::Local<v8::Object> wrapper =
- wrapper_template->NewInstance(context).ToLocalChecked();
+ ENV_OBJECT_TEMPLATE(env, wrap, wrapper_template, 2);
+
+ auto maybe_object = wrapper_template->NewInstance(context);
+ CHECK_MAYBE_EMPTY(env, maybe_object, napi_generic_failure);
+
+ v8::Local<v8::Object> wrapper = maybe_object.ToLocalChecked();
+ wrapper->SetInternalField(1, v8::External::New(isolate,
+ reinterpret_cast<void*>(const_cast<char*>(v8impl::napi_wrap_name))));
+
+ // Store the pointer as an external in the wrapper.
wrapper->SetInternalField(0, v8::External::New(isolate, native_object));
// Insert the wrapper into the object's prototype chain.
@@ -2087,16 +2130,9 @@ napi_status napi_unwrap(napi_env env, napi_value js_object, void** result) {
RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg);
v8::Local<v8::Object> obj = value.As<v8::Object>();
- // Search the object's prototype chain for the wrapper with an internal field.
- // Usually the wrapper would be the first in the chain, but it is OK for
- // other objects to be inserted in the prototype chain.
- v8::Local<v8::Object> wrapper = obj;
- do {
- v8::Local<v8::Value> proto = wrapper->GetPrototype();
- RETURN_STATUS_IF_FALSE(
- env, !proto.IsEmpty() && proto->IsObject(), napi_invalid_arg);
- wrapper = proto.As<v8::Object>();
- } while (wrapper->InternalFieldCount() != 1);
+ v8::Local<v8::Object> wrapper;
+ RETURN_STATUS_IF_FALSE(
+ env, v8impl::FindWrapper(obj, &wrapper), napi_invalid_arg);
v8::Local<v8::Value> unwrappedValue = wrapper->GetInternalField(0);
RETURN_STATUS_IF_FALSE(env, unwrappedValue->IsExternal(), napi_invalid_arg);