Index: src/runtime.cc |
=================================================================== |
--- src/runtime.cc (revision 6626) |
+++ src/runtime.cc (working copy) |
@@ -635,6 +635,90 @@ |
} |
+static bool CheckAccessException(LookupResult* result, |
+ v8::AccessType access_type) { |
+ if (result->type() == CALLBACKS) { |
+ Object* callback = result->GetCallbackObject(); |
+ if (callback->IsAccessorInfo()) { |
+ AccessorInfo* info = AccessorInfo::cast(callback); |
+ bool can_access = |
+ (access_type == v8::ACCESS_HAS && |
+ (info->all_can_read() || info->all_can_write())) || |
+ (access_type == v8::ACCESS_GET && info->all_can_read()) || |
+ (access_type == v8::ACCESS_SET && info->all_can_write()); |
+ return can_access; |
+ } |
+ } |
+ |
+ return false; |
+} |
+ |
+ |
+static bool CheckAccess(JSObject* obj, |
+ String* name, |
+ LookupResult* result, |
+ v8::AccessType access_type) { |
+ ASSERT(result->IsProperty()); |
+ |
+ JSObject* holder = result->holder(); |
+ JSObject* current = obj; |
+ while (true) { |
+ if (current->IsAccessCheckNeeded() && |
+ !Top::MayNamedAccess(current, name, access_type)) { |
+ // Access check callback denied the access, but some properties |
+ // can have a special permissions which override callbacks descision |
+ // (currently see v8::AccessControl). |
+ break; |
+ } |
+ |
+ if (current == holder) { |
+ return true; |
+ } |
+ |
+ current = JSObject::cast(current->GetPrototype()); |
+ } |
+ |
+ // API callbacks can have per callback access exceptions. |
+ switch (result->type()) { |
+ case CALLBACKS: { |
+ if (CheckAccessException(result, access_type)) { |
+ return true; |
+ } |
+ break; |
+ } |
+ case INTERCEPTOR: { |
+ // If the object has an interceptor, try real named properties. |
+ // Overwrite the result to fetch the correct property later. |
+ holder->LookupRealNamedProperty(name, result); |
+ if (result->IsProperty()) { |
+ if (CheckAccessException(result, access_type)) { |
+ return true; |
+ } |
+ } |
+ break; |
+ } |
+ default: |
+ break; |
+ } |
+ |
+ Top::ReportFailedAccessCheck(current, access_type); |
+ return false; |
+} |
+ |
+ |
+// TODO(1095): we should traverse hidden prototype hierachy as well. |
+static bool CheckElementAccess(JSObject* obj, |
+ uint32_t index, |
+ v8::AccessType access_type) { |
+ if (obj->IsAccessCheckNeeded() && |
+ !Top::MayIndexedAccess(obj, index, access_type)) { |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+ |
// Enumerator used as indices into the array returned from GetOwnProperty |
enum PropertyDescriptorIndices { |
IS_ACCESSOR_INDEX, |
@@ -677,7 +761,7 @@ |
// subsequent cases. |
Handle<JSValue> js_value = Handle<JSValue>::cast(obj); |
Handle<String> str(String::cast(js_value->value())); |
- Handle<String> substr = SubString(str, index, index+1, NOT_TENURED); |
+ Handle<String> substr = SubString(str, index, index + 1, NOT_TENURED); |
elms->set(IS_ACCESSOR_INDEX, Heap::false_value()); |
elms->set(VALUE_INDEX, *substr); |
@@ -690,8 +774,7 @@ |
case JSObject::INTERCEPTED_ELEMENT: |
case JSObject::FAST_ELEMENT: { |
elms->set(IS_ACCESSOR_INDEX, Heap::false_value()); |
- Handle<Object> element = GetElement(Handle<Object>(obj), index); |
- elms->set(VALUE_INDEX, *element); |
+ elms->set(VALUE_INDEX, *GetElement(obj, index)); |
elms->set(WRITABLE_INDEX, Heap::true_value()); |
elms->set(ENUMERABLE_INDEX, Heap::true_value()); |
elms->set(CONFIGURABLE_INDEX, Heap::true_value()); |
@@ -699,7 +782,14 @@ |
} |
case JSObject::DICTIONARY_ELEMENT: { |
- NumberDictionary* dictionary = obj->element_dictionary(); |
+ Handle<JSObject> holder = obj; |
+ if (obj->IsJSGlobalProxy()) { |
+ Object* proto = obj->GetPrototype(); |
+ if (proto->IsNull()) return Heap::undefined_value(); |
+ ASSERT(proto->IsJSGlobalObject()); |
+ holder = Handle<JSObject>(JSObject::cast(proto)); |
+ } |
+ NumberDictionary* dictionary = holder->element_dictionary(); |
int entry = dictionary->FindEntry(index); |
ASSERT(entry != NumberDictionary::kNotFound); |
PropertyDetails details = dictionary->DetailsAt(entry); |
@@ -709,14 +799,18 @@ |
FixedArray* callbacks = |
FixedArray::cast(dictionary->ValueAt(entry)); |
elms->set(IS_ACCESSOR_INDEX, Heap::true_value()); |
- elms->set(GETTER_INDEX, callbacks->get(0)); |
- elms->set(SETTER_INDEX, callbacks->get(1)); |
+ if (CheckElementAccess(*obj, index, v8::ACCESS_GET)) { |
+ elms->set(GETTER_INDEX, callbacks->get(0)); |
+ } |
+ if (CheckElementAccess(*obj, index, v8::ACCESS_SET)) { |
+ elms->set(SETTER_INDEX, callbacks->get(1)); |
+ } |
break; |
} |
case NORMAL: |
// This is a data property. |
elms->set(IS_ACCESSOR_INDEX, Heap::false_value()); |
- elms->set(VALUE_INDEX, dictionary->ValueAt(entry)); |
+ elms->set(VALUE_INDEX, *GetElement(obj, index)); |
elms->set(WRITABLE_INDEX, Heap::ToBoolean(!details.IsReadOnly())); |
break; |
default: |
@@ -737,6 +831,10 @@ |
return Heap::undefined_value(); |
} |
+ if (!CheckAccess(*obj, *name, &result, v8::ACCESS_HAS)) { |
+ return Heap::false_value(); |
+ } |
+ |
elms->set(ENUMERABLE_INDEX, Heap::ToBoolean(!result.IsDontEnum())); |
elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!result.IsDontDelete())); |
@@ -745,16 +843,22 @@ |
if (is_js_accessor) { |
// __defineGetter__/__defineSetter__ callback. |
+ elms->set(IS_ACCESSOR_INDEX, Heap::true_value()); |
+ |
FixedArray* structure = FixedArray::cast(result.GetCallbackObject()); |
- elms->set(IS_ACCESSOR_INDEX, Heap::true_value()); |
- elms->set(GETTER_INDEX, structure->get(0)); |
- elms->set(SETTER_INDEX, structure->get(1)); |
+ if (CheckAccess(*obj, *name, &result, v8::ACCESS_GET)) { |
+ elms->set(GETTER_INDEX, structure->get(0)); |
+ } |
+ if (CheckAccess(*obj, *name, &result, v8::ACCESS_SET)) { |
+ elms->set(SETTER_INDEX, structure->get(1)); |
+ } |
} else { |
elms->set(IS_ACCESSOR_INDEX, Heap::false_value()); |
elms->set(WRITABLE_INDEX, Heap::ToBoolean(!result.IsReadOnly())); |
PropertyAttributes attrs; |
Object* value; |
+ // GetProperty will check access and report any violations. |
{ MaybeObject* maybe_value = obj->GetProperty(*obj, &result, *name, &attrs); |
if (!maybe_value->ToObject(&value)) return maybe_value; |
} |