Commit 01fc2ab6 authored by yangguo@chromium.org's avatar yangguo@chromium.org

Allow allocation and GC in access check callbacks.

R=ishell@chromium.org

Review URL: https://codereview.chromium.org/234913003

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20730 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 203e4150
......@@ -458,10 +458,9 @@ MaybeHandle<FixedArray> GetKeysInFixedArrayFor(Handle<JSReceiver> object,
// Check access rights if required.
if (current->IsAccessCheckNeeded() &&
!isolate->MayNamedAccessWrapper(current,
isolate->factory()->undefined_value(),
v8::ACCESS_KEYS)) {
isolate->ReportFailedAccessCheckWrapper(current, v8::ACCESS_KEYS);
!isolate->MayNamedAccess(
current, isolate->factory()->undefined_value(), v8::ACCESS_KEYS)) {
isolate->ReportFailedAccessCheck(current, v8::ACCESS_KEYS);
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, FixedArray);
break;
}
......
......@@ -693,28 +693,41 @@ void Isolate::SetFailedAccessCheckCallback(
}
void Isolate::ReportFailedAccessCheck(JSObject* receiver, v8::AccessType type) {
static inline AccessCheckInfo* GetAccessCheckInfo(Isolate* isolate,
Handle<JSObject> receiver) {
JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
if (!constructor->shared()->IsApiFunction()) return NULL;
Object* data_obj =
constructor->shared()->get_api_func_data()->access_check_info();
if (data_obj == isolate->heap()->undefined_value()) return NULL;
return AccessCheckInfo::cast(data_obj);
}
void Isolate::ReportFailedAccessCheck(Handle<JSObject> receiver,
v8::AccessType type) {
if (!thread_local_top()->failed_access_check_callback_) return;
ASSERT(receiver->IsAccessCheckNeeded());
ASSERT(context());
// Get the data object from access check info.
JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
if (!constructor->shared()->IsApiFunction()) return;
Object* data_obj =
constructor->shared()->get_api_func_data()->access_check_info();
if (data_obj == heap_.undefined_value()) return;
HandleScope scope(this);
Handle<JSObject> receiver_handle(receiver);
Handle<Object> data(AccessCheckInfo::cast(data_obj)->data(), this);
{ VMState<EXTERNAL> state(this);
thread_local_top()->failed_access_check_callback_(
v8::Utils::ToLocal(receiver_handle),
Handle<Object> data;
{ DisallowHeapAllocation no_gc;
AccessCheckInfo* access_check_info = GetAccessCheckInfo(this, receiver);
if (!access_check_info) return;
data = handle(access_check_info->data(), this);
}
// Leaving JavaScript.
VMState<EXTERNAL> state(this);
thread_local_top()->failed_access_check_callback_(
v8::Utils::ToLocal(receiver),
type,
v8::Utils::ToLocal(data));
}
}
......@@ -724,13 +737,14 @@ enum MayAccessDecision {
static MayAccessDecision MayAccessPreCheck(Isolate* isolate,
JSObject* receiver,
Handle<JSObject> receiver,
v8::AccessType type) {
DisallowHeapAllocation no_gc;
// During bootstrapping, callback functions are not enabled yet.
if (isolate->bootstrapper()->IsActive()) return YES;
if (receiver->IsJSGlobalProxy()) {
Object* receiver_context = JSGlobalProxy::cast(receiver)->native_context();
Object* receiver_context = JSGlobalProxy::cast(*receiver)->native_context();
if (!receiver_context->IsContext()) return NO;
// Get the native context of current top context.
......@@ -748,16 +762,14 @@ static MayAccessDecision MayAccessPreCheck(Isolate* isolate,
}
bool Isolate::MayNamedAccess(JSObject* receiver, Object* key,
bool Isolate::MayNamedAccess(Handle<JSObject> receiver,
Handle<Object> key,
v8::AccessType type) {
ASSERT(receiver->IsJSGlobalProxy() || receiver->IsAccessCheckNeeded());
// The callers of this method are not expecting a GC.
DisallowHeapAllocation no_gc;
// Skip checks for hidden properties access. Note, we do not
// require existence of a context in this case.
if (key == heap_.hidden_string()) return true;
if (key.is_identical_to(factory()->hidden_string())) return true;
// Check for compatibility between the security tokens in the
// current lexical context and the accessed object.
......@@ -766,39 +778,30 @@ bool Isolate::MayNamedAccess(JSObject* receiver, Object* key,
MayAccessDecision decision = MayAccessPreCheck(this, receiver, type);
if (decision != UNKNOWN) return decision == YES;
// Get named access check callback
JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
if (!constructor->shared()->IsApiFunction()) return false;
Object* data_obj =
constructor->shared()->get_api_func_data()->access_check_info();
if (data_obj == heap_.undefined_value()) return false;
Object* fun_obj = AccessCheckInfo::cast(data_obj)->named_callback();
v8::NamedSecurityCallback callback =
v8::ToCData<v8::NamedSecurityCallback>(fun_obj);
if (!callback) return false;
HandleScope scope(this);
Handle<JSObject> receiver_handle(receiver, this);
Handle<Object> key_handle(key, this);
Handle<Object> data(AccessCheckInfo::cast(data_obj)->data(), this);
LOG(this, ApiNamedSecurityCheck(key));
bool result = false;
{
// Leaving JavaScript.
VMState<EXTERNAL> state(this);
result = callback(v8::Utils::ToLocal(receiver_handle),
v8::Utils::ToLocal(key_handle),
type,
v8::Utils::ToLocal(data));
Handle<Object> data;
v8::NamedSecurityCallback callback;
{ DisallowHeapAllocation no_gc;
AccessCheckInfo* access_check_info = GetAccessCheckInfo(this, receiver);
if (!access_check_info) return false;
Object* fun_obj = access_check_info->named_callback();
callback = v8::ToCData<v8::NamedSecurityCallback>(fun_obj);
if (!callback) return false;
data = handle(access_check_info->data(), this);
}
return result;
LOG(this, ApiNamedSecurityCheck(*key));
// Leaving JavaScript.
VMState<EXTERNAL> state(this);
return callback(v8::Utils::ToLocal(receiver),
v8::Utils::ToLocal(key),
type,
v8::Utils::ToLocal(data));
}
bool Isolate::MayIndexedAccess(JSObject* receiver,
bool Isolate::MayIndexedAccess(Handle<JSObject> receiver,
uint32_t index,
v8::AccessType type) {
ASSERT(receiver->IsJSGlobalProxy() || receiver->IsAccessCheckNeeded());
......@@ -809,34 +812,25 @@ bool Isolate::MayIndexedAccess(JSObject* receiver,
MayAccessDecision decision = MayAccessPreCheck(this, receiver, type);
if (decision != UNKNOWN) return decision == YES;
// Get indexed access check callback
JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
if (!constructor->shared()->IsApiFunction()) return false;
Object* data_obj =
constructor->shared()->get_api_func_data()->access_check_info();
if (data_obj == heap_.undefined_value()) return false;
Object* fun_obj = AccessCheckInfo::cast(data_obj)->indexed_callback();
v8::IndexedSecurityCallback callback =
v8::ToCData<v8::IndexedSecurityCallback>(fun_obj);
if (!callback) return false;
HandleScope scope(this);
Handle<JSObject> receiver_handle(receiver, this);
Handle<Object> data(AccessCheckInfo::cast(data_obj)->data(), this);
LOG(this, ApiIndexedSecurityCheck(index));
bool result = false;
{
// Leaving JavaScript.
VMState<EXTERNAL> state(this);
result = callback(v8::Utils::ToLocal(receiver_handle),
index,
type,
v8::Utils::ToLocal(data));
Handle<Object> data;
v8::IndexedSecurityCallback callback;
{ DisallowHeapAllocation no_gc;
// Get named access check callback
AccessCheckInfo* access_check_info = GetAccessCheckInfo(this, receiver);
if (!access_check_info) return false;
Object* fun_obj = access_check_info->indexed_callback();
callback = v8::ToCData<v8::IndexedSecurityCallback>(fun_obj);
if (!callback) return false;
data = handle(access_check_info->data(), this);
}
return result;
LOG(this, ApiIndexedSecurityCheck(index));
// Leaving JavaScript.
VMState<EXTERNAL> state(this);
return callback(
v8::Utils::ToLocal(receiver), index, type, v8::Utils::ToLocal(data));
}
......
......@@ -785,31 +785,15 @@ class Isolate {
// the result is false, the pending exception is guaranteed to be
// set.
// TODO(yangguo): temporary wrappers
bool MayNamedAccessWrapper(Handle<JSObject> receiver,
Handle<Object> key,
v8::AccessType type) {
return MayNamedAccess(*receiver, *key, type);
}
bool MayIndexedAccessWrapper(Handle<JSObject> receiver,
uint32_t index,
v8::AccessType type) {
return MayIndexedAccess(*receiver, index, type);
}
void ReportFailedAccessCheckWrapper(Handle<JSObject> receiver,
v8::AccessType type) {
ReportFailedAccessCheck(*receiver, type);
}
bool MayNamedAccess(JSObject* receiver,
Object* key,
bool MayNamedAccess(Handle<JSObject> receiver,
Handle<Object> key,
v8::AccessType type);
bool MayIndexedAccess(JSObject* receiver,
bool MayIndexedAccess(Handle<JSObject> receiver,
uint32_t index,
v8::AccessType type);
void SetFailedAccessCheckCallback(v8::FailedAccessCheckCallback callback);
void ReportFailedAccessCheck(JSObject* receiver, v8::AccessType type);
void ReportFailedAccessCheck(Handle<JSObject> receiver, v8::AccessType type);
// Exception throwing support. The caller should use the result
// of Throw() as its return value.
......
This diff is collapsed.
......@@ -1717,11 +1717,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetPrototype) {
ASSERT(!obj->IsAccessCheckNeeded() || obj->IsJSObject());
do {
if (obj->IsAccessCheckNeeded() &&
!isolate->MayNamedAccessWrapper(Handle<JSObject>::cast(obj),
isolate->factory()->proto_string(),
v8::ACCESS_GET)) {
isolate->ReportFailedAccessCheckWrapper(Handle<JSObject>::cast(obj),
v8::ACCESS_GET);
!isolate->MayNamedAccess(Handle<JSObject>::cast(obj),
isolate->factory()->proto_string(),
v8::ACCESS_GET)) {
isolate->ReportFailedAccessCheck(Handle<JSObject>::cast(obj),
v8::ACCESS_GET);
RETURN_IF_SCHEDULED_EXCEPTION(isolate);
return isolate->heap()->undefined_value();
}
......@@ -1749,10 +1749,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetPrototype) {
CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, prototype, 1);
if (obj->IsAccessCheckNeeded() &&
!isolate->MayNamedAccessWrapper(obj,
isolate->factory()->proto_string(),
v8::ACCESS_SET)) {
isolate->ReportFailedAccessCheckWrapper(obj, v8::ACCESS_SET);
!isolate->MayNamedAccess(
obj, isolate->factory()->proto_string(), v8::ACCESS_SET)) {
isolate->ReportFailedAccessCheck(obj, v8::ACCESS_SET);
RETURN_IF_SCHEDULED_EXCEPTION(isolate);
return isolate->heap()->undefined_value();
}
......@@ -1851,11 +1850,11 @@ static AccessCheckResult CheckPropertyAccess(Handle<JSObject> obj,
if (name->AsArrayIndex(&index)) {
// TODO(1095): we should traverse hidden prototype hierachy as well.
if (CheckGenericAccess(
obj, obj, index, access_type, &Isolate::MayIndexedAccessWrapper)) {
obj, obj, index, access_type, &Isolate::MayIndexedAccess)) {
return ACCESS_ALLOWED;
}
obj->GetIsolate()->ReportFailedAccessCheckWrapper(obj, access_type);
obj->GetIsolate()->ReportFailedAccessCheck(obj, access_type);
return ACCESS_FORBIDDEN;
}
......@@ -1866,7 +1865,7 @@ static AccessCheckResult CheckPropertyAccess(Handle<JSObject> obj,
if (!lookup.IsProperty()) return ACCESS_ABSENT;
Handle<JSObject> holder(lookup.holder(), isolate);
if (CheckGenericAccess<Handle<Object> >(
obj, holder, name, access_type, &Isolate::MayNamedAccessWrapper)) {
obj, holder, name, access_type, &Isolate::MayNamedAccess)) {
return ACCESS_ALLOWED;
}
......@@ -1894,7 +1893,7 @@ static AccessCheckResult CheckPropertyAccess(Handle<JSObject> obj,
break;
}
isolate->ReportFailedAccessCheckWrapper(obj, access_type);
isolate->ReportFailedAccessCheck(obj, access_type);
return ACCESS_FORBIDDEN;
}
......@@ -5793,10 +5792,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLocalPropertyNames) {
if (obj->IsJSGlobalProxy()) {
// Only collect names if access is permitted.
if (obj->IsAccessCheckNeeded() &&
!isolate->MayNamedAccessWrapper(obj,
isolate->factory()->undefined_value(),
v8::ACCESS_KEYS)) {
isolate->ReportFailedAccessCheckWrapper(obj, v8::ACCESS_KEYS);
!isolate->MayNamedAccess(
obj, isolate->factory()->undefined_value(), v8::ACCESS_KEYS)) {
isolate->ReportFailedAccessCheck(obj, v8::ACCESS_KEYS);
RETURN_IF_SCHEDULED_EXCEPTION(isolate);
return *isolate->factory()->NewJSArray(0);
}
......@@ -5813,10 +5811,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLocalPropertyNames) {
for (int i = 0; i < length; i++) {
// Only collect names if access is permitted.
if (jsproto->IsAccessCheckNeeded() &&
!isolate->MayNamedAccessWrapper(jsproto,
isolate->factory()->undefined_value(),
v8::ACCESS_KEYS)) {
isolate->ReportFailedAccessCheckWrapper(jsproto, v8::ACCESS_KEYS);
!isolate->MayNamedAccess(
jsproto, isolate->factory()->undefined_value(), v8::ACCESS_KEYS)) {
isolate->ReportFailedAccessCheck(jsproto, v8::ACCESS_KEYS);
RETURN_IF_SCHEDULED_EXCEPTION(isolate);
return *isolate->factory()->NewJSArray(0);
}
......@@ -5966,10 +5963,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_LocalKeys) {
if (object->IsJSGlobalProxy()) {
// Do access checks before going to the global object.
if (object->IsAccessCheckNeeded() &&
!isolate->MayNamedAccessWrapper(object,
isolate->factory()->undefined_value(),
v8::ACCESS_KEYS)) {
isolate->ReportFailedAccessCheckWrapper(object, v8::ACCESS_KEYS);
!isolate->MayNamedAccess(
object, isolate->factory()->undefined_value(), v8::ACCESS_KEYS)) {
isolate->ReportFailedAccessCheck(object, v8::ACCESS_KEYS);
RETURN_IF_SCHEDULED_EXCEPTION(isolate);
return *isolate->factory()->NewJSArray(0);
}
......@@ -14865,9 +14861,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_IsAccessAllowedForObserver) {
Handle<Object> key = args.at<Object>(2);
SaveContext save(isolate);
isolate->set_context(observer->context());
if (!isolate->MayNamedAccessWrapper(object,
isolate->factory()->undefined_value(),
v8::ACCESS_KEYS)) {
if (!isolate->MayNamedAccess(
object, isolate->factory()->undefined_value(), v8::ACCESS_KEYS)) {
return isolate->heap()->false_value();
}
bool access_allowed = false;
......@@ -14875,12 +14870,12 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_IsAccessAllowedForObserver) {
if (key->ToArrayIndex(&index) ||
(key->IsString() && String::cast(*key)->AsArrayIndex(&index))) {
access_allowed =
isolate->MayIndexedAccessWrapper(object, index, v8::ACCESS_GET) &&
isolate->MayIndexedAccessWrapper(object, index, v8::ACCESS_HAS);
isolate->MayIndexedAccess(object, index, v8::ACCESS_GET) &&
isolate->MayIndexedAccess(object, index, v8::ACCESS_HAS);
} else {
access_allowed =
isolate->MayNamedAccessWrapper(object, key, v8::ACCESS_GET) &&
isolate->MayNamedAccessWrapper(object, key, v8::ACCESS_HAS);
isolate->MayNamedAccess(object, key, v8::ACCESS_GET) &&
isolate->MayNamedAccess(object, key, v8::ACCESS_HAS);
}
return isolate->heap()->ToBoolean(access_allowed);
}
......
......@@ -8252,34 +8252,6 @@ THREADED_TEST(TypeSwitch) {
}
// For use within the TestSecurityHandler() test.
static bool g_security_callback_result = false;
static bool NamedSecurityTestCallback(Local<v8::Object> global,
Local<Value> name,
v8::AccessType type,
Local<Value> data) {
// Always allow read access.
if (type == v8::ACCESS_GET)
return true;
// Sometimes allow other access.
return g_security_callback_result;
}
static bool IndexedSecurityTestCallback(Local<v8::Object> global,
uint32_t key,
v8::AccessType type,
Local<Value> data) {
// Always allow read access.
if (type == v8::ACCESS_GET)
return true;
// Sometimes allow other access.
return g_security_callback_result;
}
static int trouble_nesting = 0;
static void TroubleCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
......@@ -8406,6 +8378,36 @@ TEST(TryCatchFinallyUsingTryCatchHandler) {
}
// For use within the TestSecurityHandler() test.
static bool g_security_callback_result = false;
static bool NamedSecurityTestCallback(Local<v8::Object> global,
Local<Value> name,
v8::AccessType type,
Local<Value> data) {
printf("a\n");
// Always allow read access.
if (type == v8::ACCESS_GET)
return true;
// Sometimes allow other access.
return g_security_callback_result;
}
static bool IndexedSecurityTestCallback(Local<v8::Object> global,
uint32_t key,
v8::AccessType type,
Local<Value> data) {
printf("b\n");
// Always allow read access.
if (type == v8::ACCESS_GET)
return true;
// Sometimes allow other access.
return g_security_callback_result;
}
// SecurityHandler can't be run twice
TEST(SecurityHandler) {
v8::Isolate* isolate = CcTest::isolate();
......@@ -8577,6 +8579,61 @@ THREADED_TEST(SecurityChecksForPrototypeChain) {
}
static bool named_security_check_with_gc_called;
static bool NamedSecurityCallbackWithGC(Local<v8::Object> global,
Local<Value> name,
v8::AccessType type,
Local<Value> data) {
CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
named_security_check_with_gc_called = true;
return true;
}
static bool indexed_security_check_with_gc_called;
static bool IndexedSecurityTestCallbackWithGC(Local<v8::Object> global,
uint32_t key,
v8::AccessType type,
Local<Value> data) {
CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
indexed_security_check_with_gc_called = true;
return true;
}
TEST(SecurityTestGCAllowed) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
v8::Handle<v8::ObjectTemplate> object_template =
v8::ObjectTemplate::New(isolate);
object_template->SetAccessCheckCallbacks(NamedSecurityCallbackWithGC,
IndexedSecurityTestCallbackWithGC);
v8::Handle<Context> context = Context::New(isolate);
v8::Context::Scope context_scope(context);
context->Global()->Set(v8_str("obj"), object_template->NewInstance());
named_security_check_with_gc_called = false;
CompileRun("obj.foo = new String(1001);");
CHECK(named_security_check_with_gc_called);
indexed_security_check_with_gc_called = false;
CompileRun("obj[0] = new String(1002);");
CHECK(indexed_security_check_with_gc_called);
named_security_check_with_gc_called = false;
CHECK(CompileRun("obj.foo")->ToString()->Equals(v8_str("1001")));
CHECK(named_security_check_with_gc_called);
indexed_security_check_with_gc_called = false;
CHECK(CompileRun("obj[0]")->ToString()->Equals(v8_str("1002")));
CHECK(indexed_security_check_with_gc_called);
}
THREADED_TEST(CrossDomainDelete) {
LocalContext env1;
v8::HandleScope handle_scope(env1->GetIsolate());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment