2016-06-27 11:48:04 +00:00
|
|
|
// Copyright 2016 the V8 project authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2021-08-23 13:01:06 +00:00
|
|
|
#include "include/v8-container.h"
|
|
|
|
#include "include/v8-external.h"
|
|
|
|
#include "include/v8-function.h"
|
2016-06-27 11:48:04 +00:00
|
|
|
#include "test/cctest/cctest.h"
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
int32_t g_cross_context_int = 0;
|
|
|
|
|
2016-07-07 19:37:05 +00:00
|
|
|
bool g_expect_interceptor_call = false;
|
|
|
|
|
2016-06-27 11:48:04 +00:00
|
|
|
void NamedGetter(v8::Local<v8::Name> property,
|
|
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
2016-07-07 19:37:05 +00:00
|
|
|
CHECK(g_expect_interceptor_call);
|
2016-06-27 11:48:04 +00:00
|
|
|
v8::Isolate* isolate = info.GetIsolate();
|
|
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
|
|
if (property->Equals(context, v8_str("cross_context_int")).FromJust())
|
|
|
|
info.GetReturnValue().Set(g_cross_context_int);
|
|
|
|
}
|
|
|
|
|
|
|
|
void NamedSetter(v8::Local<v8::Name> property, v8::Local<v8::Value> value,
|
|
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
2016-07-07 19:37:05 +00:00
|
|
|
CHECK(g_expect_interceptor_call);
|
2016-06-27 11:48:04 +00:00
|
|
|
v8::Isolate* isolate = info.GetIsolate();
|
|
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
|
|
if (!property->Equals(context, v8_str("cross_context_int")).FromJust())
|
|
|
|
return;
|
|
|
|
if (value->IsInt32()) {
|
|
|
|
g_cross_context_int = value->ToInt32(context).ToLocalChecked()->Value();
|
|
|
|
}
|
|
|
|
info.GetReturnValue().Set(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void NamedQuery(v8::Local<v8::Name> property,
|
|
|
|
const v8::PropertyCallbackInfo<v8::Integer>& info) {
|
2016-07-07 19:37:05 +00:00
|
|
|
CHECK(g_expect_interceptor_call);
|
2016-06-27 11:48:04 +00:00
|
|
|
v8::Isolate* isolate = info.GetIsolate();
|
|
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
|
|
if (!property->Equals(context, v8_str("cross_context_int")).FromJust())
|
|
|
|
return;
|
|
|
|
info.GetReturnValue().Set(v8::DontDelete);
|
|
|
|
}
|
|
|
|
|
|
|
|
void NamedDeleter(v8::Local<v8::Name> property,
|
|
|
|
const v8::PropertyCallbackInfo<v8::Boolean>& info) {
|
2016-07-07 19:37:05 +00:00
|
|
|
CHECK(g_expect_interceptor_call);
|
2016-06-27 11:48:04 +00:00
|
|
|
v8::Isolate* isolate = info.GetIsolate();
|
|
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
|
|
if (!property->Equals(context, v8_str("cross_context_int")).FromJust())
|
|
|
|
return;
|
|
|
|
info.GetReturnValue().Set(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void NamedEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info) {
|
2016-07-07 19:37:05 +00:00
|
|
|
CHECK(g_expect_interceptor_call);
|
2016-06-27 11:48:04 +00:00
|
|
|
v8::Isolate* isolate = info.GetIsolate();
|
|
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
|
|
v8::Local<v8::Array> names = v8::Array::New(isolate, 1);
|
|
|
|
names->Set(context, 0, v8_str("cross_context_int")).FromJust();
|
|
|
|
info.GetReturnValue().Set(names);
|
|
|
|
}
|
|
|
|
|
|
|
|
void IndexedGetter(uint32_t index,
|
|
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
2016-07-07 19:37:05 +00:00
|
|
|
CHECK(g_expect_interceptor_call);
|
2016-06-27 11:48:04 +00:00
|
|
|
if (index == 7) info.GetReturnValue().Set(g_cross_context_int);
|
|
|
|
}
|
|
|
|
|
|
|
|
void IndexedSetter(uint32_t index, v8::Local<v8::Value> value,
|
|
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
2016-07-07 19:37:05 +00:00
|
|
|
CHECK(g_expect_interceptor_call);
|
2016-06-27 11:48:04 +00:00
|
|
|
v8::Isolate* isolate = info.GetIsolate();
|
|
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
|
|
if (index != 7) return;
|
|
|
|
if (value->IsInt32()) {
|
|
|
|
g_cross_context_int = value->ToInt32(context).ToLocalChecked()->Value();
|
|
|
|
}
|
|
|
|
info.GetReturnValue().Set(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void IndexedQuery(uint32_t index,
|
|
|
|
const v8::PropertyCallbackInfo<v8::Integer>& info) {
|
2016-07-07 19:37:05 +00:00
|
|
|
CHECK(g_expect_interceptor_call);
|
2016-06-27 11:48:04 +00:00
|
|
|
if (index == 7) info.GetReturnValue().Set(v8::DontDelete);
|
|
|
|
}
|
|
|
|
|
|
|
|
void IndexedDeleter(uint32_t index,
|
|
|
|
const v8::PropertyCallbackInfo<v8::Boolean>& info) {
|
2016-07-07 19:37:05 +00:00
|
|
|
CHECK(g_expect_interceptor_call);
|
2016-06-27 11:48:04 +00:00
|
|
|
if (index == 7) info.GetReturnValue().Set(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void IndexedEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info) {
|
2016-07-07 19:37:05 +00:00
|
|
|
CHECK(g_expect_interceptor_call);
|
2016-06-27 11:48:04 +00:00
|
|
|
v8::Isolate* isolate = info.GetIsolate();
|
|
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
|
|
v8::Local<v8::Array> names = v8::Array::New(isolate, 1);
|
|
|
|
names->Set(context, 0, v8_str("7")).FromJust();
|
|
|
|
info.GetReturnValue().Set(names);
|
|
|
|
}
|
|
|
|
|
2017-02-22 10:13:24 +00:00
|
|
|
void MethodGetter(v8::Local<v8::Name> property,
|
|
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
|
|
v8::Isolate* isolate = info.GetIsolate();
|
|
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
|
|
|
|
|
|
v8::Local<v8::External> data = info.Data().As<v8::External>();
|
|
|
|
v8::Local<v8::FunctionTemplate>& function_template =
|
|
|
|
*reinterpret_cast<v8::Local<v8::FunctionTemplate>*>(data->Value());
|
|
|
|
|
|
|
|
info.GetReturnValue().Set(
|
|
|
|
function_template->GetFunction(context).ToLocalChecked());
|
|
|
|
}
|
|
|
|
|
|
|
|
void MethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
|
|
|
info.GetReturnValue().Set(8);
|
|
|
|
}
|
|
|
|
|
Propagate exceptions thrown by access check interceptors.
When v8 fails an access check, it invokes a helper to try to see if it
can service the request via an access check interceptor. Invoking the
access check interceptor can throw an exception (e.g. a SecurityError).
Unfortunately, the failed access check property helpers and the
interceptor helpers don't agree on how to propagate the exception: if
the interceptor helper detects a scheduled exception, it promotes the
exception to a pending exception and returns to the failed access check
property helper.
The failed access check property helper also has an early return in
case of a scheduled exception. However, this doesn't work, as the
previously thrown exception is no longer scheduled, as it's been
promoted to a pending exception. Thus, the failed access check property
helper always end up calling the failed access check callback as well.
Since Blink's implementation of the failed access check callback also
throws an exception, this conflicts with the previously-thrown,
already-pending exception.
With this patch, the failed access check property helpers check for a
pending exception rather than a scheduled exception after invoking the
interceptor, so the exception can be propagated correctly.
BUG=v8:5715
R=yangguo@chromium.org,jochen@chromium.org
Review-Url: https://codereview.chromium.org/2550423002
Cr-Commit-Position: refs/heads/master@{#41556}
2016-12-07 15:54:02 +00:00
|
|
|
void NamedGetterThrowsException(
|
|
|
|
v8::Local<v8::Name> property,
|
|
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
|
|
info.GetIsolate()->ThrowException(v8_str("exception"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void NamedSetterThrowsException(
|
|
|
|
v8::Local<v8::Name> property, v8::Local<v8::Value> value,
|
|
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
|
|
info.GetIsolate()->ThrowException(v8_str("exception"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void IndexedGetterThrowsException(
|
|
|
|
uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
|
|
info.GetIsolate()->ThrowException(v8_str("exception"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void IndexedSetterThrowsException(
|
|
|
|
uint32_t index, v8::Local<v8::Value> value,
|
|
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
|
|
info.GetIsolate()->ThrowException(v8_str("exception"));
|
|
|
|
}
|
|
|
|
|
2016-06-27 11:48:04 +00:00
|
|
|
bool AccessCheck(v8::Local<v8::Context> accessing_context,
|
|
|
|
v8::Local<v8::Object> accessed_object,
|
|
|
|
v8::Local<v8::Value> data) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GetCrossContextInt(v8::Local<v8::String> property,
|
|
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
2016-07-07 19:37:05 +00:00
|
|
|
CHECK(!g_expect_interceptor_call);
|
2016-06-27 11:48:04 +00:00
|
|
|
info.GetReturnValue().Set(g_cross_context_int);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetCrossContextInt(v8::Local<v8::String> property,
|
|
|
|
v8::Local<v8::Value> value,
|
|
|
|
const v8::PropertyCallbackInfo<void>& info) {
|
2016-07-07 19:37:05 +00:00
|
|
|
CHECK(!g_expect_interceptor_call);
|
2016-06-27 11:48:04 +00:00
|
|
|
v8::Isolate* isolate = info.GetIsolate();
|
|
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
|
|
if (value->IsInt32()) {
|
|
|
|
g_cross_context_int = value->ToInt32(context).ToLocalChecked()->Value();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Return42(v8::Local<v8::String> property,
|
|
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
|
|
info.GetReturnValue().Set(42);
|
|
|
|
}
|
|
|
|
|
2016-07-07 19:37:05 +00:00
|
|
|
void CheckCanRunScriptInContext(v8::Isolate* isolate,
|
|
|
|
v8::Local<v8::Context> context) {
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
|
|
|
g_expect_interceptor_call = false;
|
|
|
|
g_cross_context_int = 0;
|
|
|
|
|
|
|
|
// Running script in this context should work.
|
|
|
|
CompileRunChecked(isolate, "this.foo = 42; this[23] = true;");
|
|
|
|
ExpectInt32("this.all_can_read", 42);
|
|
|
|
CompileRunChecked(isolate, "this.cross_context_int = 23");
|
|
|
|
CHECK_EQ(g_cross_context_int, 23);
|
|
|
|
ExpectInt32("this.cross_context_int", 23);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckCrossContextAccess(v8::Isolate* isolate,
|
|
|
|
v8::Local<v8::Context> accessing_context,
|
|
|
|
v8::Local<v8::Object> accessed_object) {
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
accessing_context->Global()
|
|
|
|
->Set(accessing_context, v8_str("other"), accessed_object)
|
|
|
|
.FromJust();
|
|
|
|
v8::Context::Scope context_scope(accessing_context);
|
|
|
|
|
|
|
|
g_expect_interceptor_call = true;
|
|
|
|
g_cross_context_int = 23;
|
|
|
|
|
|
|
|
{
|
|
|
|
v8::TryCatch try_catch(isolate);
|
|
|
|
CHECK(CompileRun(accessing_context, "this.other.foo").IsEmpty());
|
|
|
|
}
|
|
|
|
{
|
|
|
|
v8::TryCatch try_catch(isolate);
|
|
|
|
CHECK(CompileRun(accessing_context, "this.other[23]").IsEmpty());
|
|
|
|
}
|
|
|
|
|
|
|
|
// AllCanRead properties are also inaccessible.
|
|
|
|
{
|
|
|
|
v8::TryCatch try_catch(isolate);
|
|
|
|
CHECK(CompileRun(accessing_context, "this.other.all_can_read").IsEmpty());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Intercepted properties are accessible, however.
|
|
|
|
ExpectInt32("this.other.cross_context_int", 23);
|
|
|
|
CompileRunChecked(isolate, "this.other.cross_context_int = 42");
|
|
|
|
ExpectInt32("this.other[7]", 42);
|
|
|
|
ExpectString("JSON.stringify(Object.getOwnPropertyNames(this.other))",
|
|
|
|
"[\"7\",\"cross_context_int\"]");
|
|
|
|
}
|
|
|
|
|
Propagate exceptions thrown by access check interceptors.
When v8 fails an access check, it invokes a helper to try to see if it
can service the request via an access check interceptor. Invoking the
access check interceptor can throw an exception (e.g. a SecurityError).
Unfortunately, the failed access check property helpers and the
interceptor helpers don't agree on how to propagate the exception: if
the interceptor helper detects a scheduled exception, it promotes the
exception to a pending exception and returns to the failed access check
property helper.
The failed access check property helper also has an early return in
case of a scheduled exception. However, this doesn't work, as the
previously thrown exception is no longer scheduled, as it's been
promoted to a pending exception. Thus, the failed access check property
helper always end up calling the failed access check callback as well.
Since Blink's implementation of the failed access check callback also
throws an exception, this conflicts with the previously-thrown,
already-pending exception.
With this patch, the failed access check property helpers check for a
pending exception rather than a scheduled exception after invoking the
interceptor, so the exception can be propagated correctly.
BUG=v8:5715
R=yangguo@chromium.org,jochen@chromium.org
Review-Url: https://codereview.chromium.org/2550423002
Cr-Commit-Position: refs/heads/master@{#41556}
2016-12-07 15:54:02 +00:00
|
|
|
void CheckCrossContextAccessWithException(
|
|
|
|
v8::Isolate* isolate, v8::Local<v8::Context> accessing_context,
|
|
|
|
v8::Local<v8::Object> accessed_object) {
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
accessing_context->Global()
|
|
|
|
->Set(accessing_context, v8_str("other"), accessed_object)
|
|
|
|
.FromJust();
|
|
|
|
v8::Context::Scope context_scope(accessing_context);
|
|
|
|
|
|
|
|
{
|
|
|
|
v8::TryCatch try_catch(isolate);
|
|
|
|
CompileRun("this.other.should_throw");
|
|
|
|
CHECK(try_catch.HasCaught());
|
|
|
|
CHECK(try_catch.Exception()->IsString());
|
|
|
|
CHECK(v8_str("exception")
|
|
|
|
->Equals(accessing_context, try_catch.Exception())
|
|
|
|
.FromJust());
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
v8::TryCatch try_catch(isolate);
|
|
|
|
CompileRun("this.other.should_throw = 8");
|
|
|
|
CHECK(try_catch.HasCaught());
|
|
|
|
CHECK(try_catch.Exception()->IsString());
|
|
|
|
CHECK(v8_str("exception")
|
|
|
|
->Equals(accessing_context, try_catch.Exception())
|
|
|
|
.FromJust());
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
v8::TryCatch try_catch(isolate);
|
|
|
|
CompileRun("this.other[42]");
|
|
|
|
CHECK(try_catch.HasCaught());
|
|
|
|
CHECK(try_catch.Exception()->IsString());
|
|
|
|
CHECK(v8_str("exception")
|
|
|
|
->Equals(accessing_context, try_catch.Exception())
|
|
|
|
.FromJust());
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
v8::TryCatch try_catch(isolate);
|
|
|
|
CompileRun("this.other[42] = 8");
|
|
|
|
CHECK(try_catch.HasCaught());
|
|
|
|
CHECK(try_catch.Exception()->IsString());
|
|
|
|
CHECK(v8_str("exception")
|
|
|
|
->Equals(accessing_context, try_catch.Exception())
|
|
|
|
.FromJust());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-19 12:21:52 +00:00
|
|
|
void Ctor(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
|
|
|
CHECK(info.IsConstructCall());
|
|
|
|
}
|
|
|
|
|
2016-06-27 11:48:04 +00:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
TEST(AccessCheckWithInterceptor) {
|
|
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
|
|
v8::HandleScope scope(isolate);
|
|
|
|
v8::Local<v8::ObjectTemplate> global_template =
|
|
|
|
v8::ObjectTemplate::New(isolate);
|
|
|
|
global_template->SetAccessCheckCallbackAndHandler(
|
|
|
|
AccessCheck,
|
|
|
|
v8::NamedPropertyHandlerConfiguration(
|
|
|
|
NamedGetter, NamedSetter, NamedQuery, NamedDeleter, NamedEnumerator),
|
|
|
|
v8::IndexedPropertyHandlerConfiguration(IndexedGetter, IndexedSetter,
|
|
|
|
IndexedQuery, IndexedDeleter,
|
|
|
|
IndexedEnumerator));
|
|
|
|
global_template->SetNativeDataProperty(
|
|
|
|
v8_str("cross_context_int"), GetCrossContextInt, SetCrossContextInt);
|
|
|
|
global_template->SetNativeDataProperty(
|
|
|
|
v8_str("all_can_read"), Return42, nullptr, v8::Local<v8::Value>(),
|
|
|
|
v8::None, v8::Local<v8::AccessorSignature>(), v8::ALL_CAN_READ);
|
|
|
|
|
|
|
|
v8::Local<v8::Context> context0 =
|
|
|
|
v8::Context::New(isolate, nullptr, global_template);
|
2016-07-07 19:37:05 +00:00
|
|
|
CheckCanRunScriptInContext(isolate, context0);
|
2016-06-27 11:48:04 +00:00
|
|
|
|
|
|
|
// Create another context.
|
2016-07-07 19:37:05 +00:00
|
|
|
v8::Local<v8::Context> context1 =
|
|
|
|
v8::Context::New(isolate, nullptr, global_template);
|
|
|
|
CheckCrossContextAccess(isolate, context1, context0->Global());
|
|
|
|
}
|
|
|
|
|
2017-02-22 10:13:24 +00:00
|
|
|
TEST(CallFunctionWithRemoteContextReceiver) {
|
|
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
|
|
v8::HandleScope scope(isolate);
|
|
|
|
v8::Local<v8::FunctionTemplate> global_template =
|
|
|
|
v8::FunctionTemplate::New(isolate);
|
|
|
|
|
|
|
|
v8::Local<v8::Signature> signature =
|
|
|
|
v8::Signature::New(isolate, global_template);
|
|
|
|
v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(
|
|
|
|
isolate, MethodCallback, v8::External::New(isolate, &function_template),
|
|
|
|
signature);
|
|
|
|
|
|
|
|
global_template->InstanceTemplate()->SetAccessCheckCallbackAndHandler(
|
|
|
|
AccessCheck, v8::NamedPropertyHandlerConfiguration(
|
|
|
|
MethodGetter, nullptr, nullptr, nullptr, nullptr,
|
|
|
|
v8::External::New(isolate, &function_template)),
|
|
|
|
v8::IndexedPropertyHandlerConfiguration());
|
|
|
|
|
|
|
|
v8::Local<v8::Object> accessed_object =
|
|
|
|
v8::Context::NewRemoteContext(isolate,
|
|
|
|
global_template->InstanceTemplate())
|
|
|
|
.ToLocalChecked();
|
|
|
|
v8::Local<v8::Context> accessing_context =
|
|
|
|
v8::Context::New(isolate, nullptr, global_template->InstanceTemplate());
|
|
|
|
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
accessing_context->Global()
|
|
|
|
->Set(accessing_context, v8_str("other"), accessed_object)
|
|
|
|
.FromJust();
|
|
|
|
v8::Context::Scope context_scope(accessing_context);
|
|
|
|
|
|
|
|
{
|
|
|
|
v8::TryCatch try_catch(isolate);
|
|
|
|
ExpectInt32("this.other.method()", 8);
|
|
|
|
CHECK(!try_catch.HasCaught());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Propagate exceptions thrown by access check interceptors.
When v8 fails an access check, it invokes a helper to try to see if it
can service the request via an access check interceptor. Invoking the
access check interceptor can throw an exception (e.g. a SecurityError).
Unfortunately, the failed access check property helpers and the
interceptor helpers don't agree on how to propagate the exception: if
the interceptor helper detects a scheduled exception, it promotes the
exception to a pending exception and returns to the failed access check
property helper.
The failed access check property helper also has an early return in
case of a scheduled exception. However, this doesn't work, as the
previously thrown exception is no longer scheduled, as it's been
promoted to a pending exception. Thus, the failed access check property
helper always end up calling the failed access check callback as well.
Since Blink's implementation of the failed access check callback also
throws an exception, this conflicts with the previously-thrown,
already-pending exception.
With this patch, the failed access check property helpers check for a
pending exception rather than a scheduled exception after invoking the
interceptor, so the exception can be propagated correctly.
BUG=v8:5715
R=yangguo@chromium.org,jochen@chromium.org
Review-Url: https://codereview.chromium.org/2550423002
Cr-Commit-Position: refs/heads/master@{#41556}
2016-12-07 15:54:02 +00:00
|
|
|
TEST(AccessCheckWithExceptionThrowingInterceptor) {
|
|
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
|
|
isolate->SetFailedAccessCheckCallbackFunction([](v8::Local<v8::Object> target,
|
|
|
|
v8::AccessType type,
|
|
|
|
v8::Local<v8::Value> data) {
|
2017-12-19 11:39:50 +00:00
|
|
|
UNREACHABLE(); // This should never be called.
|
Propagate exceptions thrown by access check interceptors.
When v8 fails an access check, it invokes a helper to try to see if it
can service the request via an access check interceptor. Invoking the
access check interceptor can throw an exception (e.g. a SecurityError).
Unfortunately, the failed access check property helpers and the
interceptor helpers don't agree on how to propagate the exception: if
the interceptor helper detects a scheduled exception, it promotes the
exception to a pending exception and returns to the failed access check
property helper.
The failed access check property helper also has an early return in
case of a scheduled exception. However, this doesn't work, as the
previously thrown exception is no longer scheduled, as it's been
promoted to a pending exception. Thus, the failed access check property
helper always end up calling the failed access check callback as well.
Since Blink's implementation of the failed access check callback also
throws an exception, this conflicts with the previously-thrown,
already-pending exception.
With this patch, the failed access check property helpers check for a
pending exception rather than a scheduled exception after invoking the
interceptor, so the exception can be propagated correctly.
BUG=v8:5715
R=yangguo@chromium.org,jochen@chromium.org
Review-Url: https://codereview.chromium.org/2550423002
Cr-Commit-Position: refs/heads/master@{#41556}
2016-12-07 15:54:02 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
v8::HandleScope scope(isolate);
|
|
|
|
v8::Local<v8::ObjectTemplate> global_template =
|
|
|
|
v8::ObjectTemplate::New(isolate);
|
|
|
|
global_template->SetAccessCheckCallbackAndHandler(
|
|
|
|
AccessCheck, v8::NamedPropertyHandlerConfiguration(
|
|
|
|
NamedGetterThrowsException, NamedSetterThrowsException),
|
|
|
|
v8::IndexedPropertyHandlerConfiguration(IndexedGetterThrowsException,
|
|
|
|
IndexedSetterThrowsException));
|
|
|
|
|
|
|
|
// Create two contexts.
|
|
|
|
v8::Local<v8::Context> context0 =
|
|
|
|
v8::Context::New(isolate, nullptr, global_template);
|
|
|
|
v8::Local<v8::Context> context1 =
|
|
|
|
v8::Context::New(isolate, nullptr, global_template);
|
|
|
|
|
|
|
|
CheckCrossContextAccessWithException(isolate, context1, context0->Global());
|
|
|
|
}
|
|
|
|
|
2016-07-07 19:37:05 +00:00
|
|
|
TEST(NewRemoteContext) {
|
|
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
|
|
v8::HandleScope scope(isolate);
|
|
|
|
v8::Local<v8::ObjectTemplate> global_template =
|
|
|
|
v8::ObjectTemplate::New(isolate);
|
|
|
|
global_template->SetAccessCheckCallbackAndHandler(
|
|
|
|
AccessCheck,
|
|
|
|
v8::NamedPropertyHandlerConfiguration(
|
|
|
|
NamedGetter, NamedSetter, NamedQuery, NamedDeleter, NamedEnumerator),
|
|
|
|
v8::IndexedPropertyHandlerConfiguration(IndexedGetter, IndexedSetter,
|
|
|
|
IndexedQuery, IndexedDeleter,
|
|
|
|
IndexedEnumerator));
|
|
|
|
global_template->SetNativeDataProperty(
|
|
|
|
v8_str("cross_context_int"), GetCrossContextInt, SetCrossContextInt);
|
|
|
|
global_template->SetNativeDataProperty(
|
|
|
|
v8_str("all_can_read"), Return42, nullptr, v8::Local<v8::Value>(),
|
|
|
|
v8::None, v8::Local<v8::AccessorSignature>(), v8::ALL_CAN_READ);
|
|
|
|
|
|
|
|
v8::Local<v8::Object> global0 =
|
|
|
|
v8::Context::NewRemoteContext(isolate, global_template).ToLocalChecked();
|
|
|
|
|
|
|
|
// Create a real context.
|
2016-06-27 11:48:04 +00:00
|
|
|
{
|
|
|
|
v8::HandleScope other_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context1 =
|
|
|
|
v8::Context::New(isolate, nullptr, global_template);
|
2016-07-07 19:37:05 +00:00
|
|
|
|
|
|
|
CheckCrossContextAccess(isolate, context1, global0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a context using the detached global.
|
|
|
|
{
|
|
|
|
v8::HandleScope other_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context2 =
|
|
|
|
v8::Context::New(isolate, nullptr, global_template, global0);
|
|
|
|
|
|
|
|
CheckCanRunScriptInContext(isolate, context2);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Turn a regular context into a remote context.
|
|
|
|
{
|
|
|
|
v8::HandleScope other_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context3 =
|
|
|
|
v8::Context::New(isolate, nullptr, global_template);
|
|
|
|
|
|
|
|
CheckCanRunScriptInContext(isolate, context3);
|
|
|
|
|
|
|
|
// Turn the global object into a remote context, and try to access it.
|
|
|
|
v8::Local<v8::Object> context3_global = context3->Global();
|
|
|
|
context3->DetachGlobal();
|
|
|
|
v8::Local<v8::Object> global3 =
|
|
|
|
v8::Context::NewRemoteContext(isolate, global_template, context3_global)
|
|
|
|
.ToLocalChecked();
|
|
|
|
v8::Local<v8::Context> context4 =
|
|
|
|
v8::Context::New(isolate, nullptr, global_template);
|
|
|
|
|
|
|
|
CheckCrossContextAccess(isolate, context4, global3);
|
|
|
|
|
|
|
|
// Turn it back into a regular context.
|
|
|
|
v8::Local<v8::Context> context5 =
|
|
|
|
v8::Context::New(isolate, nullptr, global_template, global3);
|
|
|
|
|
|
|
|
CheckCanRunScriptInContext(isolate, context5);
|
2016-06-27 11:48:04 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-19 12:21:52 +00:00
|
|
|
|
|
|
|
TEST(NewRemoteInstance) {
|
|
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
|
|
v8::HandleScope scope(isolate);
|
|
|
|
v8::Local<v8::FunctionTemplate> tmpl =
|
|
|
|
v8::FunctionTemplate::New(isolate, Ctor);
|
|
|
|
v8::Local<v8::ObjectTemplate> instance = tmpl->InstanceTemplate();
|
|
|
|
instance->SetAccessCheckCallbackAndHandler(
|
|
|
|
AccessCheck,
|
|
|
|
v8::NamedPropertyHandlerConfiguration(
|
|
|
|
NamedGetter, NamedSetter, NamedQuery, NamedDeleter, NamedEnumerator),
|
|
|
|
v8::IndexedPropertyHandlerConfiguration(IndexedGetter, IndexedSetter,
|
|
|
|
IndexedQuery, IndexedDeleter,
|
|
|
|
IndexedEnumerator));
|
|
|
|
tmpl->SetNativeDataProperty(
|
|
|
|
v8_str("all_can_read"), Return42, nullptr, v8::Local<v8::Value>(),
|
|
|
|
v8::None, v8::Local<v8::AccessorSignature>(), v8::ALL_CAN_READ);
|
|
|
|
|
|
|
|
v8::Local<v8::Object> obj = tmpl->NewRemoteInstance().ToLocalChecked();
|
|
|
|
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
CheckCrossContextAccess(isolate, context, obj);
|
|
|
|
}
|