add access checks to receivers on function callbacks
R=verwaest@chromium.org BUG=468451 LOG=N Review URL: https://codereview.chromium.org/1036743004 Cr-Commit-Position: refs/heads/master@{#27482}
This commit is contained in:
parent
3ad973a16f
commit
a45a1de7aa
@ -4183,6 +4183,13 @@ class V8_EXPORT FunctionTemplate : public Template {
|
||||
*/
|
||||
void SetClassName(Handle<String> name);
|
||||
|
||||
|
||||
/**
|
||||
* When set to true, no access check will be performed on the receiver of a
|
||||
* function call. Currently defaults to true, but this is subject to change.
|
||||
*/
|
||||
void SetAcceptAnyReceiver(bool value);
|
||||
|
||||
/**
|
||||
* Determines whether the __proto__ accessor ignores instances of
|
||||
* the function template. If instances of the function template are
|
||||
|
10
src/api.cc
10
src/api.cc
@ -953,6 +953,7 @@ static Local<FunctionTemplate> FunctionTemplateNew(
|
||||
obj->set_length(length);
|
||||
obj->set_undetectable(false);
|
||||
obj->set_needs_access_check(false);
|
||||
obj->set_accept_any_receiver(true);
|
||||
if (!signature.IsEmpty())
|
||||
obj->set_signature(*Utils::OpenHandle(*signature));
|
||||
return Utils::ToLocal(obj);
|
||||
@ -1126,6 +1127,15 @@ void FunctionTemplate::SetClassName(Handle<String> name) {
|
||||
}
|
||||
|
||||
|
||||
void FunctionTemplate::SetAcceptAnyReceiver(bool value) {
|
||||
auto info = Utils::OpenHandle(this);
|
||||
EnsureNotInstantiated(info, "v8::FunctionTemplate::SetAcceptAnyReceiver");
|
||||
auto isolate = info->GetIsolate();
|
||||
ENTER_V8(isolate);
|
||||
info->set_accept_any_receiver(value);
|
||||
}
|
||||
|
||||
|
||||
void FunctionTemplate::SetHiddenPrototype(bool value) {
|
||||
auto info = Utils::OpenHandle(this);
|
||||
EnsureNotInstantiated(info, "v8::FunctionTemplate::SetHiddenPrototype");
|
||||
|
@ -1044,6 +1044,17 @@ MUST_USE_RESULT static MaybeHandle<Object> HandleApiCallHelper(
|
||||
DCHECK(!args[0]->IsNull());
|
||||
if (args[0]->IsUndefined()) args[0] = function->global_proxy();
|
||||
|
||||
if (!is_construct && !fun_data->accept_any_receiver()) {
|
||||
Handle<Object> receiver(&args[0]);
|
||||
if (receiver->IsJSObject() && receiver->IsAccessCheckNeeded()) {
|
||||
Handle<JSObject> js_receiver = Handle<JSObject>::cast(receiver);
|
||||
if (!isolate->MayAccess(js_receiver)) {
|
||||
isolate->ReportFailedAccessCheck(js_receiver);
|
||||
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object* raw_holder = fun_data->GetCompatibleReceiver(isolate, args[0]);
|
||||
|
||||
if (raw_holder->IsNull()) {
|
||||
|
@ -8655,6 +8655,11 @@ bool HOptimizedGraphBuilder::TryInlineApiCall(Handle<JSFunction> function,
|
||||
CallOptimization optimization(function);
|
||||
if (!optimization.is_simple_api_call()) return false;
|
||||
Handle<Map> holder_map;
|
||||
for (int i = 0; i < receiver_maps->length(); ++i) {
|
||||
auto map = receiver_maps->at(i);
|
||||
// Don't inline calls to receivers requiring accesschecks.
|
||||
if (map->is_access_check_needed()) return false;
|
||||
}
|
||||
if (call_type == kCallApiFunction) {
|
||||
// Cannot embed a direct reference to the global proxy map
|
||||
// as it maybe dropped on deserialization.
|
||||
|
@ -5583,6 +5583,8 @@ BOOL_ACCESSORS(FunctionTemplateInfo, flag, remove_prototype,
|
||||
BOOL_ACCESSORS(FunctionTemplateInfo, flag, do_not_cache,
|
||||
kDoNotCacheBit)
|
||||
BOOL_ACCESSORS(FunctionTemplateInfo, flag, instantiated, kInstantiatedBit)
|
||||
BOOL_ACCESSORS(FunctionTemplateInfo, flag, accept_any_receiver,
|
||||
kAcceptAnyReceiver)
|
||||
BOOL_ACCESSORS(SharedFunctionInfo, start_position_and_type, is_expression,
|
||||
kIsExpressionBit)
|
||||
BOOL_ACCESSORS(SharedFunctionInfo, start_position_and_type, is_toplevel,
|
||||
|
@ -10749,6 +10749,7 @@ class FunctionTemplateInfo: public TemplateInfo {
|
||||
DECL_BOOLEAN_ACCESSORS(remove_prototype)
|
||||
DECL_BOOLEAN_ACCESSORS(do_not_cache)
|
||||
DECL_BOOLEAN_ACCESSORS(instantiated)
|
||||
DECL_BOOLEAN_ACCESSORS(accept_any_receiver)
|
||||
|
||||
DECLARE_CAST(FunctionTemplateInfo)
|
||||
|
||||
@ -10794,6 +10795,7 @@ class FunctionTemplateInfo: public TemplateInfo {
|
||||
static const int kRemovePrototypeBit = 4;
|
||||
static const int kDoNotCacheBit = 5;
|
||||
static const int kInstantiatedBit = 6;
|
||||
static const int kAcceptAnyReceiver = 7;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(FunctionTemplateInfo);
|
||||
};
|
||||
|
@ -605,3 +605,86 @@ THREADED_TEST(Regress433458) {
|
||||
"Object.defineProperty(obj, 'prop', { writable: false });"
|
||||
"Object.defineProperty(obj, 'prop', { writable: true });");
|
||||
}
|
||||
|
||||
|
||||
static bool security_check_value = false;
|
||||
|
||||
|
||||
static bool SecurityTestCallback(Local<v8::Object> global, Local<Value> name,
|
||||
v8::AccessType type, Local<Value> data) {
|
||||
return security_check_value;
|
||||
}
|
||||
|
||||
|
||||
TEST(PrototypeGetterAccessCheck) {
|
||||
i::FLAG_allow_natives_syntax = true;
|
||||
LocalContext env;
|
||||
v8::Isolate* isolate = env->GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
auto fun_templ = v8::FunctionTemplate::New(isolate);
|
||||
auto getter_templ = v8::FunctionTemplate::New(isolate, handle_property);
|
||||
getter_templ->SetAcceptAnyReceiver(false);
|
||||
fun_templ->InstanceTemplate()->SetAccessorProperty(v8_str("foo"),
|
||||
getter_templ);
|
||||
auto obj_templ = v8::ObjectTemplate::New(isolate);
|
||||
obj_templ->SetAccessCheckCallbacks(SecurityTestCallback, nullptr);
|
||||
env->Global()->Set(v8_str("Fun"), fun_templ->GetFunction());
|
||||
env->Global()->Set(v8_str("obj"), obj_templ->NewInstance());
|
||||
env->Global()->Set(v8_str("obj2"), obj_templ->NewInstance());
|
||||
|
||||
security_check_value = true;
|
||||
CompileRun("var proto = new Fun();");
|
||||
CompileRun("obj.__proto__ = proto;");
|
||||
ExpectInt32("proto.foo", 907);
|
||||
|
||||
// Test direct.
|
||||
security_check_value = true;
|
||||
ExpectInt32("obj.foo", 907);
|
||||
security_check_value = false;
|
||||
{
|
||||
v8::TryCatch try_catch(isolate);
|
||||
CompileRun("obj.foo");
|
||||
CHECK(try_catch.HasCaught());
|
||||
}
|
||||
|
||||
// Test through call.
|
||||
security_check_value = true;
|
||||
ExpectInt32("proto.__lookupGetter__('foo').call(obj)", 907);
|
||||
security_check_value = false;
|
||||
{
|
||||
v8::TryCatch try_catch(isolate);
|
||||
CompileRun("proto.__lookupGetter__('foo').call(obj)");
|
||||
CHECK(try_catch.HasCaught());
|
||||
}
|
||||
|
||||
// Test ics.
|
||||
CompileRun(
|
||||
"function f() {"
|
||||
" var x;"
|
||||
" for (var i = 0; i < 4; i++) {"
|
||||
" x = obj.foo;"
|
||||
" }"
|
||||
" return x;"
|
||||
"}");
|
||||
|
||||
security_check_value = true;
|
||||
ExpectInt32("f()", 907);
|
||||
security_check_value = false;
|
||||
{
|
||||
v8::TryCatch try_catch(isolate);
|
||||
CompileRun("f();");
|
||||
CHECK(try_catch.HasCaught());
|
||||
}
|
||||
|
||||
// Test crankshaft.
|
||||
CompileRun("%OptimizeFunctionOnNextCall(f);");
|
||||
|
||||
security_check_value = true;
|
||||
ExpectInt32("f()", 907);
|
||||
security_check_value = false;
|
||||
{
|
||||
v8::TryCatch try_catch(isolate);
|
||||
CompileRun("f();");
|
||||
CHECK(try_catch.HasCaught());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user