-
Notifications
You must be signed in to change notification settings - Fork 30k
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
[WIP] src: intercept property descriptors on sandbox #15114
Conversation
src/node_contextify.cc
Outdated
if (desc.has_configurable()) { | ||
desc_copy.set_configurable(desc.configurable()); | ||
} | ||
Maybe<bool> result = ctx->sandbox()->DefineProperty(context, property, desc_copy); |
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.
@fhinkel It feels wrong to me having to make manual copies of the PropertyDescriptor
. Am I missing something?
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.
It's more or less correct. You have to know whether you want a value or accessor descriptor. enumerable, configurable, and DefineProp() is the same for both cases though.
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.
@targos Can you refactor the copying process into a new function? I suspect it might come handy in the future, and it'll also be nice to write the DefineProperty bit only once.
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.
I haven't tried playing around with it, but it does seem like one should be able to just pass in desc
without making a copy. I'm sure there's an excellent reason why not tho :-)
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.
@jasnell I can't just pass desc
because it's a const reference. DefineProperty
wants a non-const reference.
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.
sigh... ok yeah, that's the kind of question that is easily answered by reading the docs ;-) That is unfortunate.
The failing test is here: node/test/parallel/test-vm-context.js Lines 118 to 119 in 365c245
|
I think @AnnaMag already has a PR open for this. Her PR removes the copyProperties() and thus fixes most of the vm issues. |
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.
I suppose it's okay to apply the ES6-aware parts first, before the more complete CopyProperties() removal. However, I think https://codereview.chromium.org/2807333003/ might be a crucial part of getting this fully working even not counting CopyProperties() removal, so I'd wait for @AnnaMag's reply before proceeding.
args.GetReturnValue().Set(prop_attr); | ||
if (!maybe_prop_desc.IsEmpty()) { | ||
Local<Value> prop_desc = maybe_prop_desc.ToLocalChecked(); | ||
if (!prop_desc->IsUndefined()) { |
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.
Is this check necessary?
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.
When the property does not exist on the sandbox, the property descriptor is undefined. In that case we don't intercept the call (so that Object.getOwnPropertyDescriptor(this, 'Object')
can get it from the global object)
src/node_contextify.cc
Outdated
if (desc.has_configurable()) { | ||
desc_copy.set_configurable(desc.configurable()); | ||
} | ||
Maybe<bool> result = ctx->sandbox()->DefineProperty(context, property, desc_copy); |
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.
@targos Can you refactor the copying process into a new function? I suspect it might come handy in the future, and it'll also be nice to write the DefineProperty bit only once.
src/node_contextify.cc
Outdated
if (result.IsJust()) { | ||
args.GetReturnValue().Set(result.FromJust()); | ||
} | ||
} |
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.
else { UNREACHABLE(); }
?
@TimothyGu I don't see how I can refactor the copy to a function. Do you have an idea, knowing that |
@targos... for that failing test, looking at it now. This is the bit of code that is failing: https://github.com/targos/node/blob/3eceb415fa3049764befd0e3f2149df00228d9f6/src/node_contextify.cc#L431-L432 There are two specific issues here:
I'm still looking at why the first one is happening, but for the second one, to match the expected behavior, the setter should likely throw instead of simply returning. |
@targos ... a patch like this gets the diff --git a/src/node_contextify.cc b/src/node_contextify.cc
index 10bd46b..e40039f 100644
--- a/src/node_contextify.cc
+++ b/src/node_contextify.cc
@@ -414,6 +414,7 @@ class ContextifyContext {
const PropertyCallbackInfo<Value>& args) {
ContextifyContext* ctx;
ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
+ Environment* env = Environment::GetCurrent(args.GetIsolate());
// Still initializing
if (ctx->context_.IsEmpty())
@@ -421,15 +422,16 @@ class ContextifyContext {
auto attributes = PropertyAttribute::None;
bool is_declared =
- ctx->global_proxy()->GetRealNamedPropertyAttributes(ctx->context(),
- property)
+ ctx->sandbox()->GetRealNamedPropertyAttributes(ctx->context(), property)
.To(&attributes);
bool read_only =
static_cast<int>(attributes) &
static_cast<int>(PropertyAttribute::ReadOnly);
-
- if (is_declared && read_only)
+ if (is_declared && read_only) {
+ if (args.ShouldThrowOnError())
+ env->ThrowError("Cannot assign to read only property");
return;
+ }
// true for x = 5
// false for this.x = 5
diff --git a/test/parallel/test-vm-context.js b/test/parallel/test-vm-context.js
index 7e54047..9e770f5 100644
--- a/test/parallel/test-vm-context.js
+++ b/test/parallel/test-vm-context.js
@@ -116,5 +116,5 @@ vm.runInContext('x = 0', ctx); // Does not throw but x...
assert.strictEqual(vm.runInContext('x', ctx), 42); // ...should be unaltered.
assert.throws(() => vm.runInContext('"use strict"; x = 0', ctx),
- /Cannot assign to read only property 'x'/);
+ /Cannot assign to read only property/);
assert.strictEqual(vm.runInContext('x', ctx), 42); To make the error message match perfectly, we would need to grab the name of the property (just slightly trickier with Symbol props) and include that in the error message. |
@jasnell thanks. I made that change. I also added a check to the PropertyDefinerCallback for when the property is non-configurable. |
While I was working on this, I found a possibly unexpected behavior with calls like Edit: currently, the three tests return |
@targos In case you want to go down the route of throwing the error manually, V8 uses the following algorithm: "Cannot assign to read only property '%s' of %s '%s'",
property->ToDetailString(), ctx->sandbox()->TypeOf(), ctx->sandbox()->ToDetailString() Still not a fan though. Also note that certain other errors need to be checked as well, such as an accessor property w/o a setter. |
I'll have to look into the |
So... I did what I could for the error messages with my limited C++ knowledge... Please make suggestions if you know how to improve that :) @TimothyGu thank you, we're almost there! Compared to the error thrown by V8, only the last string is inconsistent: |
src/node_contextify.cc
Outdated
inline const char* ToString(Local<Value> value) { | ||
String::Utf8Value utf8(value); | ||
return *utf8; | ||
} |
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.
Uh – This won’t work. :/ Once utf8
goes out of scope, it also potentially frees the memory used for storing value
in its UTF-8 form … I think you need to inline this everywhere (i.e. create distinct Utf8Value
s for the different values).
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.
Or, alternatively, you could probably do this (warning, haven’t compiled or tried it):
std::ostream& operator<<(std::ostream& os, Local<Value> value) {
String::Utf8Value utf8(value);
return os << *utf8;
}
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.
Thanks. That seems to work.
Hmm. ToDetailString uses the same algorithm as V8 when figuring out what to print... in particular it looks at the |
@TimothyGu I thought it could be because the wrong context was used so I tried with the main context and the contextified one but the result is the same :( |
Confirmed this condition is not met: Line 361 in a4e923f
@nodejs/v8 any idea? |
Ping @nodejs/v8 |
@hashseed Any ideas? |
I am not sure how we can properly progress further. It seems hard to get any further insight into the issue. @targos is the current status quo of the PR already better than the situation before? |
My apologies for the delay in my reaction to this. |
Closing in favor of #16293. |
Remove the CopyProperties() hack in the vm module, i.e., Contextify. Use different V8 API methods, that allow interception of DefineProperty() and do not flatten accessor descriptors to property descriptors. Move many known issues to test cases. Factor out the last test in test-vm-context.js for nodejs#10223 into its own file, test-vm-strict-assign.js. Part of this CL is taken from a stalled PR by https://github.com/AnnaMag nodejs#13265 This PR requires a backport of https://chromium.googlesource.com/v8/v8/+/37a3a15c3e52e2146e45f41c427f24414e4d7f6f Refs: nodejs#6283 Refs: nodejs#15114 Refs: nodejs#13265 Fixes: nodejs#2734 Fixes: nodejs#10223 Fixes: nodejs#11803 Fixes: nodejs#11902
Remove the CopyProperties() hack in the vm module, i.e., Contextify. Use different V8 API methods, that allow interception of DefineProperty() and do not flatten accessor descriptors to property descriptors. Move many known issues to test cases. Factor out the last test in test-vm-context.js for nodejs#10223 into its own file, test-vm-strict-assign.js. Part of this CL is taken from a stalled PR by https://github.com/AnnaMag nodejs#13265 This PR requires a backport of https://chromium.googlesource.com/v8/v8/+/37a3a15c3e52e2146e45f41c427f24414e4d7f6f PR-URL: nodejs#16293 Fixes: nodejs#2734 Fixes: nodejs#10223 Fixes: nodejs#11803 Fixes: nodejs#11902 Ref: nodejs#6283 Ref: nodejs#15114 Ref: nodejs#13265 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Timothy Gu <timothygu99@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com>
Remove the CopyProperties() hack in the vm module, i.e., Contextify. Use different V8 API methods, that allow interception of DefineProperty() and do not flatten accessor descriptors to property descriptors. Move many known issues to test cases. Factor out the last test in test-vm-context.js for nodejs/node#10223 into its own file, test-vm-strict-assign.js. Part of this CL is taken from a stalled PR by https://github.com/AnnaMag nodejs/node#13265 This PR requires a backport of https://chromium.googlesource.com/v8/v8/+/37a3a15c3e52e2146e45f41c427f24414e4d7f6f PR-URL: nodejs/node#16293 Fixes: nodejs/node#2734 Fixes: nodejs/node#10223 Fixes: nodejs/node#11803 Fixes: nodejs/node#11902 Ref: nodejs/node#6283 Ref: nodejs/node#15114 Ref: nodejs/node#13265 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Timothy Gu <timothygu99@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com>
Remove the CopyProperties() hack in the vm module, i.e., Contextify. Use different V8 API methods, that allow interception of DefineProperty() and do not flatten accessor descriptors to property descriptors. Move many known issues to test cases. Factor out the last test in test-vm-context.js for nodejs/node#10223 into its own file, test-vm-strict-assign.js. Part of this CL is taken from a stalled PR by https://github.com/AnnaMag nodejs/node#13265 This PR requires a backport of https://chromium.googlesource.com/v8/v8/+/37a3a15c3e52e2146e45f41c427f24414e4d7f6f PR-URL: nodejs/node#16293 Fixes: nodejs/node#2734 Fixes: nodejs/node#10223 Fixes: nodejs/node#11803 Fixes: nodejs/node#11902 Ref: nodejs/node#6283 Ref: nodejs/node#15114 Ref: nodejs/node#13265 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Timothy Gu <timothygu99@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com>
The main intent of this PR is to fix #2734.
It's not completely ready because one existing test is broken. I'm opening the PR now to get some help.
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)
src,vm