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:
dcarney 2015-03-26 08:21:54 -07:00 committed by Commit bot
parent 3ad973a16f
commit a45a1de7aa
7 changed files with 120 additions and 0 deletions

View File

@ -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

View File

@ -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");

View File

@ -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()) {

View File

@ -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.

View File

@ -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,

View File

@ -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);
};

View File

@ -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());
}
}