Clean up access checks.
Change-Id: Ia62ba6c8f1d0a9bfbbfde2d7be4c52c0f982b9d2
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index b1117a2..b5d9fdf 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4186,7 +4186,7 @@
mirror::Class* referring_class = referrer->GetDeclaringClass();
if (!referring_class->CanAccess(methods_class)) {
ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class,
- referrer, resolved, type);
+ resolved, type);
return NULL;
} else if (!referring_class->CanAccessMember(methods_class,
resolved->GetAccessFlags())) {
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 0419dab..dd832df 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -136,7 +136,6 @@
}
void ThrowIllegalAccessErrorClassForMethodDispatch(mirror::Class* referrer, mirror::Class* accessed,
- const mirror::ArtMethod* caller,
const mirror::ArtMethod* called,
InvokeType type) {
std::ostringstream msg;
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index 3164f30..7f13891 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -76,7 +76,6 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowIllegalAccessErrorClassForMethodDispatch(mirror::Class* referrer, mirror::Class* accessed,
- const mirror::ArtMethod* caller,
const mirror::ArtMethod* called,
InvokeType type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index 8304229..09be56e 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -226,28 +226,14 @@
return nullptr;
}
mirror::Class* referring_class = referrer->GetDeclaringClass();
- if (UNLIKELY(!referring_class->CanAccess(fields_class) ||
- !referring_class->CanAccessMember(fields_class,
- resolved_field->GetAccessFlags()))) {
- // The referring class can't access the resolved field, this may occur as a result of a
- // protected field being made public by a sub-class. Resort to the dex file to determine
- // the correct class for the access check.
- const DexFile& dex_file = *referring_class->GetDexCache()->GetDexFile();
- fields_class = class_linker->ResolveType(dex_file,
- dex_file.GetFieldId(field_idx).class_idx_,
- referring_class);
- if (UNLIKELY(!referring_class->CanAccess(fields_class))) {
- ThrowIllegalAccessErrorClass(referring_class, fields_class);
- return nullptr; // failure
- } else if (UNLIKELY(!referring_class->CanAccessMember(fields_class,
- resolved_field->GetAccessFlags()))) {
- ThrowIllegalAccessErrorField(referring_class, resolved_field);
- return nullptr; // failure
- }
+ if (UNLIKELY(!referring_class->CanAccessResolvedField<true>(fields_class, resolved_field,
+ field_idx))) {
+ DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
+ return nullptr; // Failure.
}
if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class))) {
ThrowIllegalAccessErrorFinalField(referrer, resolved_field);
- return nullptr; // failure
+ return nullptr; // Failure.
} else {
FieldHelper fh(resolved_field);
if (UNLIKELY(fh.IsPrimitiveType() != is_primitive ||
@@ -259,7 +245,7 @@
expected_size * (32 / sizeof(int32_t)),
is_primitive ? "primitive" : "non-primitive",
PrettyField(resolved_field, true).c_str());
- return nullptr; // failure
+ return nullptr; // Failure.
}
}
}
@@ -277,7 +263,7 @@
return resolved_field;
} else {
DCHECK(self->IsExceptionPending()); // Throw exception and unwind
- return nullptr; // failure
+ return nullptr; // Failure.
}
}
}
@@ -330,26 +316,10 @@
}
mirror::Class* methods_class = resolved_method->GetDeclaringClass();
mirror::Class* referring_class = referrer->GetDeclaringClass();
- if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
- !referring_class->CanAccessMember(methods_class,
- resolved_method->GetAccessFlags()))) {
- // The referring class can't access the resolved method, this may occur as a result of a
- // protected method being made public by implementing an interface that re-declares the
- // method public. Resort to the dex file to determine the correct class for the access check
- const DexFile& dex_file = *referring_class->GetDexCache()->GetDexFile();
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- methods_class = class_linker->ResolveType(dex_file,
- dex_file.GetMethodId(method_idx).class_idx_,
- referring_class);
- if (UNLIKELY(!referring_class->CanAccess(methods_class))) {
- ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class,
- referrer, resolved_method, type);
- return nullptr; // Failure.
- } else if (UNLIKELY(!referring_class->CanAccessMember(methods_class,
- resolved_method->GetAccessFlags()))) {
- ThrowIllegalAccessErrorMethod(referring_class, resolved_method);
- return nullptr; // Failure.
- }
+ if (!referring_class->CanAccessResolvedMethod<true, type>(methods_class, resolved_method,
+ method_idx)) {
+ DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
+ return nullptr; // Failure.
}
}
switch (type) {
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index e0fab8c..cd44ebc 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -22,6 +22,7 @@
#include "art_field.h"
#include "art_method.h"
#include "class_loader.h"
+#include "common_throws.h"
#include "dex_cache.h"
#include "gc/heap-inl.h"
#include "iftable.h"
@@ -202,6 +203,68 @@
return IsArrayAssignableFromArray(src);
}
+template <bool throw_on_failure>
+inline bool Class::CanAccessResolvedField(Class* access_to, ArtField* field,
+ uint32_t field_idx) {
+ if (UNLIKELY(!this->CanAccess(access_to))) {
+ // The referrer class can't access the field's declaring class but may still be able
+ // to access the field if the FieldId specifies an accessible subclass of the declaring
+ // class rather than the declaring class itself.
+ DexCache* referrer_dex_cache = this->GetDexCache();
+ uint32_t class_idx = referrer_dex_cache->GetDexFile()->GetFieldId(field_idx).class_idx_;
+ // The referenced class has already been resolved with the field, get it from the dex cache.
+ Class* dex_access_to = referrer_dex_cache->GetResolvedType(class_idx);
+ DCHECK(dex_access_to != nullptr);
+ if (UNLIKELY(!this->CanAccess(dex_access_to))) {
+ if (throw_on_failure) {
+ ThrowIllegalAccessErrorClass(this, dex_access_to);
+ }
+ return false;
+ }
+ DCHECK_EQ(this->CanAccessMember(access_to, field->GetAccessFlags()),
+ this->CanAccessMember(dex_access_to, field->GetAccessFlags()));
+ }
+ if (LIKELY(this->CanAccessMember(access_to, field->GetAccessFlags()))) {
+ return true;
+ }
+ if (throw_on_failure) {
+ ThrowIllegalAccessErrorField(this, field);
+ }
+ return false;
+}
+
+template <bool throw_on_failure, InvokeType throw_invoke_type>
+inline bool Class::CanAccessResolvedMethod(Class* access_to, ArtMethod* method,
+ uint32_t method_idx) {
+ COMPILE_ASSERT(throw_on_failure || throw_invoke_type == kStatic, non_default_throw_invoke_type);
+ if (UNLIKELY(!this->CanAccess(access_to))) {
+ // The referrer class can't access the method's declaring class but may still be able
+ // to access the method if the MethodId specifies an accessible subclass of the declaring
+ // class rather than the declaring class itself.
+ DexCache* referrer_dex_cache = this->GetDexCache();
+ uint32_t class_idx = referrer_dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_;
+ // The referenced class has already been resolved with the method, get it from the dex cache.
+ Class* dex_access_to = referrer_dex_cache->GetResolvedType(class_idx);
+ DCHECK(dex_access_to != nullptr);
+ if (UNLIKELY(!this->CanAccess(dex_access_to))) {
+ if (throw_on_failure) {
+ ThrowIllegalAccessErrorClassForMethodDispatch(this, dex_access_to,
+ method, throw_invoke_type);
+ }
+ return false;
+ }
+ DCHECK_EQ(this->CanAccessMember(access_to, method->GetAccessFlags()),
+ this->CanAccessMember(dex_access_to, method->GetAccessFlags()));
+ }
+ if (LIKELY(this->CanAccessMember(access_to, method->GetAccessFlags()))) {
+ return true;
+ }
+ if (throw_on_failure) {
+ ThrowIllegalAccessErrorMethod(this, method);
+ }
+ return false;
+}
+
inline bool Class::IsSubClass(const Class* klass) const {
DCHECK(!IsInterface()) << PrettyClass(this);
DCHECK(!IsArrayClass()) << PrettyClass(this);
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 9aa23d9..d751363 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -18,6 +18,7 @@
#define ART_RUNTIME_MIRROR_CLASS_H_
#include "gc/heap.h"
+#include "invoke_type.h"
#include "modifiers.h"
#include "object.h"
#include "primitive.h"
@@ -449,6 +450,20 @@
return this->IsInSamePackage(access_to);
}
+ // Can this class access a resolved field?
+ // Note that access to field's class is checked and this may require looking up the class
+ // referenced by the FieldId in the DexFile in case the declaring class is inaccessible.
+ template <bool throw_on_failure>
+ bool CanAccessResolvedField(Class* access_to, ArtField* field,
+ uint32_t field_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Can this class access a resolved method?
+ // Note that access to methods's class is checked and this may require looking up the class
+ // referenced by the MethodId in the DexFile in case the declaring class is inaccessible.
+ template <bool throw_on_failure, InvokeType throw_invoke_type = kStatic>
+ bool CanAccessResolvedMethod(Class* access_to, ArtMethod* resolved_method,
+ uint32_t method_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
bool IsSubClass(const Class* klass) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);