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

"in" operator in VM uses [[Get]] instead of [[GetOwnProperty]] #17480

Closed
TimothyGu opened this issue Dec 6, 2017 · 6 comments
Closed

"in" operator in VM uses [[Get]] instead of [[GetOwnProperty]] #17480

TimothyGu opened this issue Dec 6, 2017 · 6 comments
Labels
v8 engine Issues and PRs related to the V8 dependency. vm Issues and PRs related to the vm subsystem.

Comments

@TimothyGu
Copy link
Member

  • Version: master
  • Platform: all
  • Subsystem: vm
const globals = {};
const handlers = {};
const realHandlers = Reflect.ownKeys(Reflect).reduce((handlers, p) => {
  handlers[p] = (t, ...args) => {
    // Avoid printing the Receiver argument, which can lead to an infinite loop.
    console.log(p, ...(p === 'get' || p === 'set' ? args.slice(0, -1) : args));
    return Reflect[p](t, ...args);
  };
  return handlers;
}, {});
const proxy = vm.createContext(new Proxy(globals, handlers));

// Indirection needed to mitigate against #17465
// https://github.com/nodejs/node/issues/17465
const globalProxy = vm.runInContext('this', proxy);
for (const k of Reflect.ownKeys(globalProxy)) {
  Object.defineProperty(globals, k, Object.getOwnPropertyDescriptor(globalProxy, k));
}
Object.assign(handlers, realHandlers);

'a' in proxy;
  // prints "has a"
  // returns false

vm.runInContext('"a" in this', proxy);
  // prints "get a"
  // returns true (because of https://github.com/nodejs/node/issues/17465)

By spec, the in operator uses the object's [[HasProperty]] internal method, which I overrode with a Proxy trap. That's why the bare 'a' in proxy prints has a.

On the other hand, V8 doesn't quite support overriding [[HasProperty]] with the modern version of the NamedPropertyHandlerConfiguration constructor, only

  • [[Get]] getter
  • [[Set]] setter
  • [[GetOwnProperty]] descriptor
  • [[Delete]] deleter
  • [[OwnPropertyKeys]] enumerator
  • [[DefineOwnProperty]] definer

Thus I would expect the default [[HasProperty]] internal method to be used, i.e. OrdinaryHasProperty, which in turns calls the object's [[GetOwnProperty]] internal method. I.e. the descriptor should be called. Yet this isn't the case shown above -- only GenericNamedPropertyGetterCallback is called.

/cc @fhinkel @verwaest

@TimothyGu TimothyGu added v8 engine Issues and PRs related to the V8 dependency. vm Issues and PRs related to the vm subsystem. labels Dec 6, 2017
@fhinkel
Copy link
Member

fhinkel commented Dec 6, 2017

Thanks for reporting! I'll have a look.

@TimothyGu
Copy link
Member Author

@fhinkel Thanks. I've got a patch for V8 (and a smaller one for Node.js) that fixes both this issue and #17481. After this patch, vm.runInContext('"a" in this', proxy); prints getOwnPropertyDescriptor a instead, as expected.

Let me know what you think:

Patch
diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc
index 28c1cd681f..cbc182fe45 100644
--- a/deps/v8/src/objects.cc
+++ b/deps/v8/src/objects.cc
@@ -1798,6 +1798,32 @@ Maybe<PropertyAttributes> GetPropertyAttributesWithInterceptorInternal(
       CHECK(result->ToInt32(&value));
       return Just(static_cast<PropertyAttributes>(value));
     }
+  } else if (!interceptor->descriptor()->IsUndefined(isolate)) {
+    Handle<Object> result;
+    if (it->IsElement()) {
+      uint32_t index = it->index();
+      v8::IndexedPropertyDescriptorCallback descriptorCallback =
+          v8::ToCData<v8::IndexedPropertyDescriptorCallback>(
+              interceptor->descriptor());
+
+      result = args.Call(descriptorCallback, index);
+    } else {
+      Handle<Name> name = it->name();
+      DCHECK(!name->IsPrivate());
+      v8::GenericNamedPropertyDescriptorCallback descriptorCallback =
+          v8::ToCData<v8::GenericNamedPropertyDescriptorCallback>(
+              interceptor->descriptor());
+      result = args.Call(descriptorCallback, name);
+    }
+    if (!result.is_null()) {
+      PropertyDescriptor desc;
+      Utils::ApiCheck(
+          PropertyDescriptor::ToPropertyDescriptor(isolate, result, &desc),
+          it->IsElement() ? "v8::IndexedPropertyDescriptorCallback"
+                          : "v8::NamedPropertyDescriptorCallback",
+          "Invalid property descriptor.");
+      return Just(desc.ToAttributes());
+    }
   } else if (!interceptor->getter()->IsUndefined(isolate)) {
     // TODO(verwaest): Use GetPropertyWithInterceptor?
     Handle<Object> result;
@@ -7444,65 +7470,6 @@ Maybe<bool> JSReceiver::GetOwnPropertyDescriptor(Isolate* isolate,
   return GetOwnPropertyDescriptor(&it, desc);
 }
 
-namespace {
-
-Maybe<bool> GetPropertyDescriptorWithInterceptor(LookupIterator* it,
-                                                 PropertyDescriptor* desc) {
-  bool has_access = true;
-  if (it->state() == LookupIterator::ACCESS_CHECK) {
-    has_access = it->HasAccess() || JSObject::AllCanRead(it);
-    it->Next();
-  }
-
-  if (has_access && it->state() == LookupIterator::INTERCEPTOR) {
-    Isolate* isolate = it->isolate();
-    Handle<InterceptorInfo> interceptor = it->GetInterceptor();
-    if (!interceptor->descriptor()->IsUndefined(isolate)) {
-      Handle<Object> result;
-      Handle<JSObject> holder = it->GetHolder<JSObject>();
-
-      Handle<Object> receiver = it->GetReceiver();
-      if (!receiver->IsJSReceiver()) {
-        ASSIGN_RETURN_ON_EXCEPTION_VALUE(
-            isolate, receiver, Object::ConvertReceiver(isolate, receiver),
-            Nothing<bool>());
-      }
-
-      PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
-                                     *holder, Object::DONT_THROW);
-      if (it->IsElement()) {
-        uint32_t index = it->index();
-        v8::IndexedPropertyDescriptorCallback descriptorCallback =
-            v8::ToCData<v8::IndexedPropertyDescriptorCallback>(
-                interceptor->descriptor());
-
-        result = args.Call(descriptorCallback, index);
-      } else {
-        Handle<Name> name = it->name();
-        DCHECK(!name->IsPrivate());
-        v8::GenericNamedPropertyDescriptorCallback descriptorCallback =
-            v8::ToCData<v8::GenericNamedPropertyDescriptorCallback>(
-                interceptor->descriptor());
-        result = args.Call(descriptorCallback, name);
-      }
-      if (!result.is_null()) {
-        // Request successfully intercepted, try to set the property
-        // descriptor.
-        Utils::ApiCheck(
-            PropertyDescriptor::ToPropertyDescriptor(isolate, result, desc),
-            it->IsElement() ? "v8::IndexedPropertyDescriptorCallback"
-                            : "v8::NamedPropertyDescriptorCallback",
-            "Invalid property descriptor.");
-
-        return Just(true);
-      }
-    }
-  }
-  it->Restart();
-  return Just(false);
-}
-}  // namespace
-
 // ES6 9.1.5.1
 // Returns true on success, false if the property didn't exist, nothing if
 // an exception was thrown.
@@ -7516,12 +7483,6 @@ Maybe<bool> JSReceiver::GetOwnPropertyDescriptor(LookupIterator* it,
                                              it->GetName(), desc);
   }
 
-  Maybe<bool> intercepted = GetPropertyDescriptorWithInterceptor(it, desc);
-  MAYBE_RETURN(intercepted, Nothing<bool>());
-  if (intercepted.FromJust()) {
-    return Just(true);
-  }
-
   // Request was not intercepted, continue as normal.
   // 1. (Assert)
   // 2. If O does not have an own property with key P, return undefined.
diff --git a/src/node_contextify.cc b/src/node_contextify.cc
index 73c5fe08ab..8451fbe1a9 100644
--- a/src/node_contextify.cc
+++ b/src/node_contextify.cc
@@ -396,10 +396,10 @@ class ContextifyContext {
 
     Local<Object> sandbox = ctx->sandbox();
 
-    if (sandbox->HasOwnProperty(context, property).FromMaybe(false)) {
-      args.GetReturnValue().Set(
-          sandbox->GetOwnPropertyDescriptor(context, property)
-              .ToLocalChecked());
+    auto maybe_desc = sandbox->GetOwnPropertyDescriptor(context, property);
+    Local<Value> desc;
+    if (maybe_desc.ToLocal(&desc) && desc->IsObject()) {
+      args.GetReturnValue().Set(desc);
     }
   }
 

@TimothyGu
Copy link
Member Author

TimothyGu commented Dec 8, 2017

I've submitted a revised version of the patch above as a CL to V8 https://chromium-review.googlesource.com/c/v8/v8/+/816515, which fixes both this bug and #17481.

@fhinkel review requested and appreciated :)

@fhinkel
Copy link
Member

fhinkel commented Dec 21, 2017

Thanks! Landed in https://chromium.googlesource.com/v8/v8/+/d5fbf7c5c3f8f9b46b75f674771f3533c7e3e24d. Let's give it some canary coverage, then we can back port it.

@targos
Copy link
Member

targos commented Apr 27, 2018

The patch was reverted in https://chromium-review.googlesource.com/c/v8/v8/+/850355 because of a performance regression.

TimothyGu added a commit to TimothyGu/node that referenced this issue Aug 18, 2018
This allows using a Proxy object as the sandbox for a VM context.

Fixes: nodejs#17480
Fixes: nodejs#17481
TimothyGu pushed a commit that referenced this issue Aug 24, 2018
Original commit message:

    [api][runtime]  Support all-in ctors of {Named,Indexed}PropertyHandlerConfiguration

    - Explicitly allows construction of
    {Named,Indexed}PropertyHandlerConfiguration with all the members filled.

    Bug: v8:7612
    Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng
    Change-Id: I426ea33846b5dbf2b3482c722c963a6e4b0abded
    Reviewed-on: https://chromium-review.googlesource.com/1163882
    Reviewed-by: Toon Verwaest <verwaest@chromium.org>
    Reviewed-by: Adam Klein <adamk@chromium.org>
    Commit-Queue: Camillo Bruni <cbruni@chromium.org>
    Cr-Commit-Position: refs/heads/master@{#55142}

PR-URL: #22390
Fixes: #17480
Fixes: #17481
Refs: v8/v8@e1a7699
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: James M Snell <jasnell@gmail.com>
TimothyGu added a commit that referenced this issue Aug 24, 2018
This allows using a Proxy object as the sandbox for a VM context.

PR-URL: #22390
Fixes: #17480
Fixes: #17481
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: James M Snell <jasnell@gmail.com>
@TimothyGu
Copy link
Member Author

Well, actually closed in 85c356c / #22390.

targos pushed a commit that referenced this issue Aug 24, 2018
Original commit message:

    [api] Avoid needlessly calling descriptor interceptors

    Reland part of https://chromium-review.googlesource.com/c/v8/v8/+/816515.

    Change-Id: I72ad85ffd162fc0563fc25cdf35189e894f9dc82
    Reviewed-on: https://chromium-review.googlesource.com/1138808
    Commit-Queue: Timothy Gu <timothygu@chromium.org>
    Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
    Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
    Cr-Commit-Position: refs/heads/master@{#54492}

PR-URL: #22390
Fixes: #17480
Fixes: #17481
Refs: v8/v8@9eb96bb
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: James M Snell <jasnell@gmail.com>
targos pushed a commit that referenced this issue Aug 24, 2018
Original commit message:

    [api][runtime]  Support all-in ctors of {Named,Indexed}PropertyHandlerConfiguration

    - Explicitly allows construction of
    {Named,Indexed}PropertyHandlerConfiguration with all the members filled.

    Bug: v8:7612
    Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng
    Change-Id: I426ea33846b5dbf2b3482c722c963a6e4b0abded
    Reviewed-on: https://chromium-review.googlesource.com/1163882
    Reviewed-by: Toon Verwaest <verwaest@chromium.org>
    Reviewed-by: Adam Klein <adamk@chromium.org>
    Commit-Queue: Camillo Bruni <cbruni@chromium.org>
    Cr-Commit-Position: refs/heads/master@{#55142}

PR-URL: #22390
Fixes: #17480
Fixes: #17481
Refs: v8/v8@e1a7699
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: James M Snell <jasnell@gmail.com>
targos pushed a commit that referenced this issue Aug 24, 2018
This allows using a Proxy object as the sandbox for a VM context.

PR-URL: #22390
Fixes: #17480
Fixes: #17481
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: James M Snell <jasnell@gmail.com>
targos pushed a commit that referenced this issue Sep 3, 2018
Original commit message:

    [api] Avoid needlessly calling descriptor interceptors

    Reland part of https://chromium-review.googlesource.com/c/v8/v8/+/816515.

    Change-Id: I72ad85ffd162fc0563fc25cdf35189e894f9dc82
    Reviewed-on: https://chromium-review.googlesource.com/1138808
    Commit-Queue: Timothy Gu <timothygu@chromium.org>
    Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
    Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
    Cr-Commit-Position: refs/heads/master@{#54492}

PR-URL: #22390
Fixes: #17480
Fixes: #17481
Refs: v8/v8@9eb96bb
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: James M Snell <jasnell@gmail.com>
targos pushed a commit that referenced this issue Sep 3, 2018
Original commit message:

    [api][runtime]  Support all-in ctors of {Named,Indexed}PropertyHandlerConfiguration

    - Explicitly allows construction of
    {Named,Indexed}PropertyHandlerConfiguration with all the members filled.

    Bug: v8:7612
    Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng
    Change-Id: I426ea33846b5dbf2b3482c722c963a6e4b0abded
    Reviewed-on: https://chromium-review.googlesource.com/1163882
    Reviewed-by: Toon Verwaest <verwaest@chromium.org>
    Reviewed-by: Adam Klein <adamk@chromium.org>
    Commit-Queue: Camillo Bruni <cbruni@chromium.org>
    Cr-Commit-Position: refs/heads/master@{#55142}

PR-URL: #22390
Fixes: #17480
Fixes: #17481
Refs: v8/v8@e1a7699
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: James M Snell <jasnell@gmail.com>
targos pushed a commit that referenced this issue Sep 3, 2018
This allows using a Proxy object as the sandbox for a VM context.

PR-URL: #22390
Fixes: #17480
Fixes: #17481
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: James M Snell <jasnell@gmail.com>
targos pushed a commit to targos/node that referenced this issue Sep 7, 2018
Original commit message:

    [api][runtime]  Support all-in ctors of {Named,Indexed}PropertyHandlerConfiguration

    - Explicitly allows construction of
    {Named,Indexed}PropertyHandlerConfiguration with all the members filled.

    Bug: v8:7612
    Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng
    Change-Id: I426ea33846b5dbf2b3482c722c963a6e4b0abded
    Reviewed-on: https://chromium-review.googlesource.com/1163882
    Reviewed-by: Toon Verwaest <verwaest@chromium.org>
    Reviewed-by: Adam Klein <adamk@chromium.org>
    Commit-Queue: Camillo Bruni <cbruni@chromium.org>
    Cr-Commit-Position: refs/heads/master@{nodejs#55142}

PR-URL: nodejs#22390
Fixes: nodejs#17480
Fixes: nodejs#17481
Refs: v8/v8@e1a7699
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: James M Snell <jasnell@gmail.com>
targos pushed a commit to targos/node that referenced this issue Sep 7, 2018
Original commit message:

    [api][runtime]  Support all-in ctors of {Named,Indexed}PropertyHandlerConfiguration

    - Explicitly allows construction of
    {Named,Indexed}PropertyHandlerConfiguration with all the members filled.

    Bug: v8:7612
    Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng
    Change-Id: I426ea33846b5dbf2b3482c722c963a6e4b0abded
    Reviewed-on: https://chromium-review.googlesource.com/1163882
    Reviewed-by: Toon Verwaest <verwaest@chromium.org>
    Reviewed-by: Adam Klein <adamk@chromium.org>
    Commit-Queue: Camillo Bruni <cbruni@chromium.org>
    Cr-Commit-Position: refs/heads/master@{nodejs#55142}

PR-URL: nodejs#22390
Fixes: nodejs#17480
Fixes: nodejs#17481
Refs: v8/v8@e1a7699
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: James M Snell <jasnell@gmail.com>
addaleax pushed a commit to addaleax/node that referenced this issue Sep 18, 2018
This is a re-land of a commit landed as part of
nodejs#22390.

---

This allows using a Proxy object as the sandbox for a VM context.

Refs: nodejs#22390
Fixes: nodejs#17480
Fixes: nodejs#17481
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: James M Snell <jasnell@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
v8 engine Issues and PRs related to the V8 dependency. vm Issues and PRs related to the vm subsystem.
Projects
None yet
Development

No branches or pull requests

3 participants