Fix bug in protected field access.

Change-Id: I38b094e54025d26950c0d8c8bb79d2de81d28428
diff --git a/src/compiler.cc b/src/compiler.cc
index 3980bac..7369535 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -628,17 +628,31 @@
   Field* resolved_field = ComputeReferrerField(mUnit, field_idx);
   if (resolved_field != NULL) {
     Class* referrer_class = ComputeReferrerClass(mUnit);
-    // Try to resolve referring class then access check, failure to pass the
-    Class* fields_class = resolved_field->GetDeclaringClass();
-    bool is_write_to_final_from_wrong_class = is_put && resolved_field->IsFinal() &&
-                                              fields_class != referrer_class;
-    if (referrer_class != NULL && referrer_class->CanAccess(fields_class) &&
-        referrer_class->CanAccessMember(fields_class, resolved_field->GetAccessFlags()) &&
-        !is_write_to_final_from_wrong_class) {
-      field_offset = resolved_field->GetOffset().Int32Value();
-      is_volatile = resolved_field->IsVolatile();
-      stats_->ResolvedInstanceField();
-      return true;  // Fast path.
+    if (referrer_class != NULL) {
+      Class* fields_class = resolved_field->GetDeclaringClass();
+      bool access_ok = referrer_class->CanAccess(fields_class) &&
+                       referrer_class->CanAccessMember(fields_class,
+                                                       resolved_field->GetAccessFlags());
+      if (!access_ok) {
+        // 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 = mUnit->class_linker_->FindDexFile(referrer_class->GetDexCache());
+        Class* dex_fields_class = mUnit->class_linker_->ResolveType(dex_file,
+                                                         dex_file.GetFieldId(field_idx).class_idx_,
+                                                         referrer_class);
+        access_ok = referrer_class->CanAccess(dex_fields_class) &&
+                    referrer_class->CanAccessMember(dex_fields_class,
+                                                    resolved_field->GetAccessFlags());
+      }
+      bool is_write_to_final_from_wrong_class = is_put && resolved_field->IsFinal() &&
+          fields_class != referrer_class;
+      if (access_ok && !is_write_to_final_from_wrong_class) {
+        field_offset = resolved_field->GetOffset().Int32Value();
+        is_volatile = resolved_field->IsVolatile();
+        stats_->ResolvedInstanceField();
+        return true;  // Fast path.
+      }
     }
   }
   // Clean up any exception left by field/type resolution
@@ -672,10 +686,25 @@
         stats_->ResolvedLocalStaticField();
         return true;  // fast path
       } else {
+        bool access_ok = referrer_class->CanAccess(fields_class) &&
+                         referrer_class->CanAccessMember(fields_class,
+                                                         resolved_field->GetAccessFlags());
+        if (!access_ok) {
+          // 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. Don't change the field's class as that is
+          // used to identify the SSB.
+          const DexFile& dex_file = mUnit->class_linker_->FindDexFile(referrer_class->GetDexCache());
+          Class* dex_fields_class =
+              mUnit->class_linker_->ResolveType(dex_file,
+                                                dex_file.GetFieldId(field_idx).class_idx_,
+                                                referrer_class);
+          access_ok = referrer_class->CanAccess(dex_fields_class) &&
+                      referrer_class->CanAccessMember(dex_fields_class,
+                                                      resolved_field->GetAccessFlags());
+        }
         bool is_write_to_final_from_wrong_class = is_put && resolved_field->IsFinal();
-        if (referrer_class->CanAccess(fields_class) &&
-            referrer_class->CanAccessMember(fields_class, resolved_field->GetAccessFlags()) &&
-            !is_write_to_final_from_wrong_class) {
+        if (access_ok && !is_write_to_final_from_wrong_class) {
           // We have the resolved field, we must make it into a ssbIndex for the referrer
           // in its static storage base (which may fail if it doesn't have a slot for it)
           // TODO: for images we can elide the static storage base null check
@@ -689,7 +718,8 @@
             stats_->ResolvedStaticField();
             return true;
           }
-          // Search dex file for localized ssb index
+          // Search dex file for localized ssb index, may fail if field's class is a parent
+          // of the class mentioned in the dex file and there is no dex cache entry.
           std::string descriptor(FieldHelper(resolved_field).GetDeclaringClassDescriptor());
           const DexFile::StringId* string_id =
           mUnit->dex_file_->FindStringId(descriptor);
diff --git a/src/oat/runtime/support_dexcache.cc b/src/oat/runtime/support_dexcache.cc
index e5f2f82..49e038d 100644
--- a/src/oat/runtime/support_dexcache.cc
+++ b/src/oat/runtime/support_dexcache.cc
@@ -21,13 +21,16 @@
 
 extern "C" Class* artInitializeStaticStorageFromCode(uint32_t type_idx, const Method* referrer,
                                                      Thread* self, Method** sp) {
+  // Called to ensure static storage base is initialized for direct static field reads and writes.
+  // A class may be accessing another class' fields when it doesn't have access, as access has been
+  // given by inheritance.
   FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
-  return ResolveVerifyAndClinit(type_idx, referrer, self, true, true);
+  return ResolveVerifyAndClinit(type_idx, referrer, self, true, false);
 }
 
 extern "C" Class* artInitializeTypeFromCode(uint32_t type_idx, const Method* referrer, Thread* self,
                                             Method** sp) {
-  // Called when method->dex_cache_resolved_types_[] misses
+  // Called when method->dex_cache_resolved_types_[] misses.
   FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
   return ResolveVerifyAndClinit(type_idx, referrer, self, false, false);
 }
@@ -36,7 +39,7 @@
                                                            const Method* referrer, Thread* self,
                                                            Method** sp) {
   // Called when caller isn't guaranteed to have access to a type and the dex cache may be
-  // unpopulated
+  // unpopulated.
   FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
   return ResolveVerifyAndClinit(type_idx, referrer, self, false, true);
 }
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index 38a680e..8c2b30c 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -357,14 +357,26 @@
   } else {
     Class* fields_class = resolved_field->GetDeclaringClass();
     Class* referring_class = referrer->GetDeclaringClass();
-    if (UNLIKELY(!referring_class->CanAccess(fields_class))) {
-      ThrowNewIllegalAccessErrorClass(self, referring_class, fields_class);
-      return NULL;  // failure
-    } else if (UNLIKELY(!referring_class->CanAccessMember(fields_class,
-                                                          resolved_field->GetAccessFlags()))) {
-      ThrowNewIllegalAccessErrorField(self, referring_class, resolved_field);
-      return NULL;  // failure
-    } else if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class))) {
+    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 = class_linker->FindDexFile(referring_class->GetDexCache());
+      fields_class = class_linker->ResolveType(dex_file,
+                                               dex_file.GetFieldId(field_idx).class_idx_,
+                                               referring_class);
+      if (UNLIKELY(!referring_class->CanAccess(fields_class))) {
+        ThrowNewIllegalAccessErrorClass(self, referring_class, fields_class);
+        return NULL;  // failure
+      } else if (UNLIKELY(!referring_class->CanAccessMember(fields_class,
+                                                            resolved_field->GetAccessFlags()))) {
+        ThrowNewIllegalAccessErrorField(self, referring_class, resolved_field);
+        return NULL;  // failure
+      }
+    }
+    if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class))) {
       ThrowNewIllegalAccessErrorFinalField(self, referrer, resolved_field);
       return NULL;  // failure
     } else {