diff options
Diffstat (limited to 'runtime')
| -rw-r--r-- | runtime/Android.bp | 1 | ||||
| -rw-r--r-- | runtime/class_linker.cc | 302 | ||||
| -rw-r--r-- | runtime/class_linker.h | 9 | ||||
| -rw-r--r-- | runtime/class_loader_utils.h | 116 | ||||
| -rw-r--r-- | runtime/common_runtime_test.cc | 58 | ||||
| -rw-r--r-- | runtime/debug_print.cc | 113 | ||||
| -rw-r--r-- | runtime/debug_print.h | 34 | ||||
| -rw-r--r-- | runtime/entrypoints/quick/quick_trampoline_entrypoints.cc | 22 | ||||
| -rw-r--r-- | runtime/hidden_api.cc | 116 | ||||
| -rw-r--r-- | runtime/hidden_api.h | 54 | ||||
| -rw-r--r-- | runtime/hidden_api_test.cc | 34 | ||||
| -rw-r--r-- | runtime/interpreter/unstarted_runtime.cc | 6 | ||||
| -rw-r--r-- | runtime/jni_internal.cc | 7 | ||||
| -rw-r--r-- | runtime/native/java_lang_Class.cc | 35 | ||||
| -rw-r--r-- | runtime/runtime.h | 3 | ||||
| -rw-r--r-- | runtime/verifier/method_verifier.cc | 12 | ||||
| -rw-r--r-- | runtime/well_known_classes.cc | 6 | ||||
| -rw-r--r-- | runtime/well_known_classes.h | 3 |
18 files changed, 591 insertions, 340 deletions
diff --git a/runtime/Android.bp b/runtime/Android.bp index c0f1c366b8..08025824c3 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -46,6 +46,7 @@ cc_defaults { "class_table.cc", "common_throws.cc", "compiler_filter.cc", + "debug_print.cc", "debugger.cc", "dex/art_dex_file_loader.cc", "dex/dex_file_annotations.cc", diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 879301c4a0..d98e0b2c54 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -53,6 +53,7 @@ #include "class_loader_utils.h" #include "class_table-inl.h" #include "compiler_callbacks.h" +#include "debug_print.h" #include "debugger.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" @@ -1124,12 +1125,8 @@ static bool FlattenPathClassLoader(ObjPtr<mirror::ClassLoader> class_loader, DCHECK(out_dex_file_names != nullptr); DCHECK(error_msg != nullptr); ScopedObjectAccessUnchecked soa(Thread::Current()); - ArtField* const dex_path_list_field = - jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList); - ArtField* const dex_elements_field = - jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements); - CHECK(dex_path_list_field != nullptr); - CHECK(dex_elements_field != nullptr); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::ClassLoader> handle(hs.NewHandle(class_loader)); while (!ClassLinker::IsBootClassLoader(soa, class_loader)) { if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader) != class_loader->GetClass()) { @@ -1138,32 +1135,29 @@ static bool FlattenPathClassLoader(ObjPtr<mirror::ClassLoader> class_loader, // Unsupported class loader. return false; } - ObjPtr<mirror::Object> dex_path_list = dex_path_list_field->GetObject(class_loader); - if (dex_path_list != nullptr) { - // DexPathList has an array dexElements of Elements[] which each contain a dex file. - ObjPtr<mirror::Object> dex_elements_obj = dex_elements_field->GetObject(dex_path_list); - // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look - // at the mCookie which is a DexFile vector. - if (dex_elements_obj != nullptr) { - ObjPtr<mirror::ObjectArray<mirror::Object>> dex_elements = - dex_elements_obj->AsObjectArray<mirror::Object>(); - // Reverse order since we insert the parent at the front. - for (int32_t i = dex_elements->GetLength() - 1; i >= 0; --i) { - ObjPtr<mirror::Object> element = dex_elements->GetWithoutChecks(i); - if (element == nullptr) { - *error_msg = StringPrintf("Null dex element at index %d", i); - return false; - } - ObjPtr<mirror::String> name; - if (!GetDexPathListElementName(element, &name)) { - *error_msg = StringPrintf("Invalid dex path list element at index %d", i); - return false; - } - if (name != nullptr) { - out_dex_file_names->push_front(name.Ptr()); - } - } + // Get element names. Sets error to true on failure. + auto add_element_names = [&](ObjPtr<mirror::Object> element, bool* error) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (element == nullptr) { + *error_msg = "Null dex element"; + *error = true; // Null element is a critical error. + return false; // Had an error, stop the visit. } + ObjPtr<mirror::String> name; + if (!GetDexPathListElementName(element, &name)) { + *error_msg = "Invalid dex path list element"; + *error = false; // Invalid element is not a critical error. + return false; // Stop the visit. + } + if (name != nullptr) { + out_dex_file_names->push_front(name.Ptr()); + } + return true; // Continue with the next Element. + }; + bool error = VisitClassLoaderDexElements(soa, handle, add_element_names, /* error */ false); + if (error) { + // An error occurred during DexPathList Element visiting. + return false; } class_loader = class_loader->GetParent(); } @@ -2473,71 +2467,33 @@ ObjPtr<mirror::Class> ClassLinker::FindClassInBaseDexClassLoaderClassPath( const char* descriptor, size_t hash, Handle<mirror::ClassLoader> class_loader) { - CHECK(IsPathOrDexClassLoader(soa, class_loader) || IsDelegateLastClassLoader(soa, class_loader)) + DCHECK(IsPathOrDexClassLoader(soa, class_loader) || IsDelegateLastClassLoader(soa, class_loader)) << "Unexpected class loader for descriptor " << descriptor; - Thread* self = soa.Self(); - ArtField* const cookie_field = - jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie); - ArtField* const dex_file_field = - jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); - ObjPtr<mirror::Object> dex_path_list = - jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)-> - GetObject(class_loader.Get()); - if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) { - // DexPathList has an array dexElements of Elements[] which each contain a dex file. - ObjPtr<mirror::Object> dex_elements_obj = - jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)-> - GetObject(dex_path_list); - // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look - // at the mCookie which is a DexFile vector. - if (dex_elements_obj != nullptr) { - StackHandleScope<1> hs(self); - Handle<mirror::ObjectArray<mirror::Object>> dex_elements = - hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>()); - for (int32_t i = 0; i < dex_elements->GetLength(); ++i) { - ObjPtr<mirror::Object> element = dex_elements->GetWithoutChecks(i); - if (element == nullptr) { - // Should never happen, fall back to java code to throw a NPE. - break; - } - ObjPtr<mirror::Object> dex_file = dex_file_field->GetObject(element); - if (dex_file != nullptr) { - ObjPtr<mirror::LongArray> long_array = cookie_field->GetObject(dex_file)->AsLongArray(); - if (long_array == nullptr) { - // This should never happen so log a warning. - LOG(WARNING) << "Null DexFile::mCookie for " << descriptor; - break; - } - int32_t long_array_size = long_array->GetLength(); - // First element is the oat file. - for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) { - const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>( - long_array->GetWithoutChecks(j))); - const DexFile::ClassDef* dex_class_def = - OatDexFile::FindClassDef(*cp_dex_file, descriptor, hash); - if (dex_class_def != nullptr) { - ObjPtr<mirror::Class> klass = DefineClass(self, - descriptor, - hash, - class_loader, - *cp_dex_file, - *dex_class_def); - if (klass == nullptr) { - CHECK(self->IsExceptionPending()) << descriptor; - self->ClearException(); - // TODO: Is it really right to break here, and not check the other dex files? - return nullptr; - } - return klass; - } - } - } - } - } - self->AssertNoPendingException(); - } - return nullptr; + ObjPtr<mirror::Class> ret; + auto define_class = [&](const DexFile* cp_dex_file) REQUIRES_SHARED(Locks::mutator_lock_) { + const DexFile::ClassDef* dex_class_def = + OatDexFile::FindClassDef(*cp_dex_file, descriptor, hash); + if (dex_class_def != nullptr) { + ObjPtr<mirror::Class> klass = DefineClass(soa.Self(), + descriptor, + hash, + class_loader, + *cp_dex_file, + *dex_class_def); + if (klass == nullptr) { + CHECK(soa.Self()->IsExceptionPending()) << descriptor; + soa.Self()->ClearException(); + // TODO: Is it really right to break here, and not check the other dex files? + } + ret = klass; + return false; // Found a Class (or error == nullptr), stop visit. + } + return true; // Continue with the next DexFile. + }; + + VisitClassLoaderDexFiles(soa, class_loader, define_class); + return ret; } mirror::Class* ClassLinker::FindClass(Thread* self, @@ -7878,106 +7834,6 @@ ObjPtr<mirror::Class> ClassLinker::DoResolveType(dex::TypeIndex type_idx, return resolved; } -std::string DescribeSpace(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { - std::ostringstream oss; - gc::Heap* heap = Runtime::Current()->GetHeap(); - gc::space::ContinuousSpace* cs = heap->FindContinuousSpaceFromAddress(klass.Ptr()); - if (cs != nullptr) { - if (cs->IsImageSpace()) { - oss << "image;" << cs->GetName() << ";" << cs->AsImageSpace()->GetImageFilename(); - } else { - oss << "continuous;" << cs->GetName(); - } - } else { - gc::space::DiscontinuousSpace* ds = - heap->FindDiscontinuousSpaceFromObject(klass, /* fail_ok */ true); - if (ds != nullptr) { - oss << "discontinuous;" << ds->GetName(); - } else { - oss << "invalid"; - } - } - return oss.str(); -} - -std::string DescribeLoaders(ObjPtr<mirror::ClassLoader> loader, const char* class_descriptor) - REQUIRES_SHARED(Locks::mutator_lock_) { - std::ostringstream oss; - uint32_t hash = ComputeModifiedUtf8Hash(class_descriptor); - ObjPtr<mirror::Class> path_class_loader = - WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_PathClassLoader); - ObjPtr<mirror::Class> dex_class_loader = - WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexClassLoader); - ObjPtr<mirror::Class> delegate_last_class_loader = - WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DelegateLastClassLoader); - - // Print the class loader chain. - bool found_class = false; - const char* loader_separator = ""; - if (loader == nullptr) { - oss << "BootClassLoader"; // This would be unexpected. - } - for (; loader != nullptr; loader = loader->GetParent()) { - oss << loader_separator << loader->GetClass()->PrettyDescriptor(); - loader_separator = ";"; - // If we didn't find the interface yet, try to find it in the current class loader. - if (!found_class) { - ClassTable* table = Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(loader); - ObjPtr<mirror::Class> klass = - (table != nullptr) ? table->Lookup(class_descriptor, hash) : nullptr; - if (klass != nullptr) { - found_class = true; - oss << "[hit:" << DescribeSpace(klass) << "]"; - } - } - - // For PathClassLoader, DexClassLoader or DelegateLastClassLoader - // also dump the dex file locations. - if (loader->GetClass() == path_class_loader || - loader->GetClass() == dex_class_loader || - loader->GetClass() == delegate_last_class_loader) { - ArtField* const cookie_field = - jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie); - ArtField* const dex_file_field = - jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); - ObjPtr<mirror::Object> dex_path_list = - jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)-> - GetObject(loader); - if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) { - ObjPtr<mirror::Object> dex_elements_obj = - jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)-> - GetObject(dex_path_list); - if (dex_elements_obj != nullptr) { - ObjPtr<mirror::ObjectArray<mirror::Object>> dex_elements = - dex_elements_obj->AsObjectArray<mirror::Object>(); - oss << "("; - const char* path_separator = ""; - for (int32_t i = 0; i != dex_elements->GetLength(); ++i) { - ObjPtr<mirror::Object> element = dex_elements->GetWithoutChecks(i); - ObjPtr<mirror::Object> dex_file = - (element != nullptr) ? dex_file_field->GetObject(element) : nullptr; - ObjPtr<mirror::LongArray> long_array = - (dex_file != nullptr) ? cookie_field->GetObject(dex_file)->AsLongArray() : nullptr; - if (long_array != nullptr) { - int32_t long_array_size = long_array->GetLength(); - // First element is the oat file. - for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) { - const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>( - static_cast<uintptr_t>(long_array->GetWithoutChecks(j))); - oss << path_separator << cp_dex_file->GetLocation(); - path_separator = ":"; - } - } - } - oss << ")"; - } - } - } - } - - return oss.str(); -} - ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr<mirror::Class> klass, ObjPtr<mirror::DexCache> dex_cache, ObjPtr<mirror::ClassLoader> class_loader, @@ -7993,8 +7849,8 @@ ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr<mirror::Class> klass, } DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr); if (resolved != nullptr && - hiddenapi::ShouldBlockAccessToMember( - resolved, class_loader, dex_cache, hiddenapi::kLinking)) { + hiddenapi::GetMemberAction( + resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) { resolved = nullptr; } if (resolved != nullptr) { @@ -8017,6 +7873,40 @@ ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr<mirror::Class> klass, return resolved; } +// Returns true if `method` is either null or hidden. +// Does not print any warnings if it is hidden. +static bool CheckNoSuchMethod(ArtMethod* method, + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_) { + return method == nullptr || + hiddenapi::GetMemberAction(method, + class_loader, + dex_cache, + hiddenapi::kNone) // do not print warnings + == hiddenapi::kDeny; +} + +ArtMethod* ClassLinker::FindIncompatibleMethod(ObjPtr<mirror::Class> klass, + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader, + uint32_t method_idx) { + if (klass->IsInterface()) { + ArtMethod* method = klass->FindClassMethod(dex_cache, method_idx, image_pointer_size_); + return CheckNoSuchMethod(method, dex_cache, class_loader) ? nullptr : method; + } else { + // If there was an interface method with the same signature, we would have + // found it in the "copied" methods. Only DCHECK that the interface method + // really does not exist. + if (kIsDebugBuild) { + ArtMethod* method = + klass->FindInterfaceMethod(dex_cache, method_idx, image_pointer_size_); + DCHECK(CheckNoSuchMethod(method, dex_cache, class_loader)); + } + return nullptr; + } +} + template <ClassLinker::ResolveMode kResolveMode> ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, Handle<mirror::DexCache> dex_cache, @@ -8092,13 +7982,7 @@ ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, // If we had a method, or if we can find one with another lookup type, // it's an incompatible-class-change error. if (resolved == nullptr) { - if (klass->IsInterface()) { - resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, pointer_size); - } else { - // If there was an interface method with the same signature, - // we would have found it also in the "copied" methods. - DCHECK(klass->FindInterfaceMethod(dex_cache.Get(), method_idx, pointer_size) == nullptr); - } + resolved = FindIncompatibleMethod(klass, dex_cache.Get(), class_loader.Get(), method_idx); } if (resolved != nullptr) { ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer); @@ -8136,8 +8020,8 @@ ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(uint32_t method_idx, resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, image_pointer_size_); } if (resolved != nullptr && - hiddenapi::ShouldBlockAccessToMember( - resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) { + hiddenapi::GetMemberAction( + resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) { resolved = nullptr; } return resolved; @@ -8216,8 +8100,8 @@ ArtField* ClassLinker::ResolveField(uint32_t field_idx, } if (resolved == nullptr || - hiddenapi::ShouldBlockAccessToMember( - resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) { + hiddenapi::GetMemberAction( + resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) { const char* name = dex_file.GetFieldName(field_id); const char* type = dex_file.GetFieldTypeDescriptor(field_id); ThrowNoSuchFieldError(is_static ? "static " : "instance ", klass, type, name); @@ -8250,8 +8134,8 @@ ArtField* ClassLinker::ResolveFieldJLS(uint32_t field_idx, StringPiece type(dex_file.GetFieldTypeDescriptor(field_id)); resolved = mirror::Class::FindField(self, klass, name, type); if (resolved != nullptr && - hiddenapi::ShouldBlockAccessToMember( - resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) { + hiddenapi::GetMemberAction( + resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) { resolved = nullptr; } if (resolved != nullptr) { diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 2471f146aa..c46e8271e5 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -321,6 +321,15 @@ class ClassLinker { uint32_t method_idx) REQUIRES_SHARED(Locks::mutator_lock_); + // Find a method using the wrong lookup mechanism. If `klass` is an interface, + // search for a class method. If it is a class, search for an interface method. + // This is useful when throwing IncompatibleClassChangeError. + ArtMethod* FindIncompatibleMethod(ObjPtr<mirror::Class> klass, + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader, + uint32_t method_idx) + REQUIRES_SHARED(Locks::mutator_lock_); + // Resolve a method with a given ID from the DexFile associated with the given DexCache // and ClassLoader, storing the result in DexCache. The ClassLinker and ClassLoader are // used as in ResolveType. What is unique is the method type argument which is used to diff --git a/runtime/class_loader_utils.h b/runtime/class_loader_utils.h index d160a511de..1439f11636 100644 --- a/runtime/class_loader_utils.h +++ b/runtime/class_loader_utils.h @@ -17,8 +17,12 @@ #ifndef ART_RUNTIME_CLASS_LOADER_UTILS_H_ #define ART_RUNTIME_CLASS_LOADER_UTILS_H_ +#include "art_field-inl.h" +#include "base/mutex.h" #include "handle_scope.h" +#include "jni_internal.h" #include "mirror/class_loader.h" +#include "native/dalvik_system_DexFile.h" #include "scoped_thread_state_change-inl.h" #include "well_known_classes.h" @@ -26,7 +30,7 @@ namespace art { // Returns true if the given class loader is either a PathClassLoader or a DexClassLoader. // (they both have the same behaviour with respect to class lockup order) -static bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa, +inline bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa, Handle<mirror::ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_) { mirror::Class* class_loader_class = class_loader->GetClass(); @@ -37,7 +41,7 @@ static bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa, soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader)); } -static bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa, +inline bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa, Handle<mirror::ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_) { mirror::Class* class_loader_class = class_loader->GetClass(); @@ -45,6 +49,114 @@ static bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa, soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader); } +// Visit the DexPathList$Element instances in the given classloader with the given visitor. +// Constraints on the visitor: +// * The visitor should return true to continue visiting more Elements. +// * The last argument of the visitor is an out argument of RetType. It will be returned +// when the visitor ends the visit (by returning false). +// This function assumes that the given classloader is a subclass of BaseDexClassLoader! +template <typename Visitor, typename RetType> +inline RetType VisitClassLoaderDexElements(ScopedObjectAccessAlreadyRunnable& soa, + Handle<mirror::ClassLoader> class_loader, + Visitor fn, + RetType defaultReturn) + REQUIRES_SHARED(Locks::mutator_lock_) { + Thread* self = soa.Self(); + ObjPtr<mirror::Object> dex_path_list = + jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)-> + GetObject(class_loader.Get()); + if (dex_path_list != nullptr) { + // DexPathList has an array dexElements of Elements[] which each contain a dex file. + ObjPtr<mirror::Object> dex_elements_obj = + jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)-> + GetObject(dex_path_list); + // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look + // at the mCookie which is a DexFile vector. + if (dex_elements_obj != nullptr) { + StackHandleScope<1> hs(self); + Handle<mirror::ObjectArray<mirror::Object>> dex_elements = + hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>()); + for (int32_t i = 0; i < dex_elements->GetLength(); ++i) { + ObjPtr<mirror::Object> element = dex_elements->GetWithoutChecks(i); + if (element == nullptr) { + // Should never happen, fail. + break; + } + RetType ret_value; + if (!fn(element, &ret_value)) { + return ret_value; + } + } + } + self->AssertNoPendingException(); + } + return defaultReturn; +} + +// Visit the DexFiles in the given classloader with the given visitor. +// Constraints on the visitor: +// * The visitor should return true to continue visiting more DexFiles. +// * The last argument of the visitor is an out argument of RetType. It will be returned +// when the visitor ends the visit (by returning false). +// This function assumes that the given classloader is a subclass of BaseDexClassLoader! +template <typename Visitor, typename RetType> +inline RetType VisitClassLoaderDexFiles(ScopedObjectAccessAlreadyRunnable& soa, + Handle<mirror::ClassLoader> class_loader, + Visitor fn, + RetType defaultReturn) + REQUIRES_SHARED(Locks::mutator_lock_) { + ArtField* const cookie_field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie); + ArtField* const dex_file_field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); + if (dex_file_field == nullptr || cookie_field == nullptr) { + return defaultReturn; + } + auto visit_dex_files = [&](ObjPtr<mirror::Object> element, RetType* ret) + REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr<mirror::Object> dex_file = dex_file_field->GetObject(element); + if (dex_file != nullptr) { + ObjPtr<mirror::LongArray> long_array = cookie_field->GetObject(dex_file)->AsLongArray(); + if (long_array == nullptr) { + // This should never happen so log a warning. + LOG(WARNING) << "Null DexFile::mCookie"; + *ret = defaultReturn; + return true; + } + int32_t long_array_size = long_array->GetLength(); + // First element is the oat file. + for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) { + const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>( + long_array->GetWithoutChecks(j))); + RetType ret_value; + if (!fn(cp_dex_file, /* out */ &ret_value)) { + *ret = ret_value; + return false; + } + } + } + return true; + }; + + return VisitClassLoaderDexElements(soa, class_loader, visit_dex_files, defaultReturn); +} + +// Simplified version of the above, w/o out argument. +template <typename Visitor> +inline void VisitClassLoaderDexFiles(ScopedObjectAccessAlreadyRunnable& soa, + Handle<mirror::ClassLoader> class_loader, + Visitor fn) + REQUIRES_SHARED(Locks::mutator_lock_) { + auto helper = [&fn](const art::DexFile* dex_file, void** ATTRIBUTE_UNUSED) + REQUIRES_SHARED(Locks::mutator_lock_) { + return fn(dex_file); + }; + VisitClassLoaderDexFiles<decltype(helper), void*>(soa, + class_loader, + helper, + /* default */ nullptr); +} + } // namespace art #endif // ART_RUNTIME_CLASS_LOADER_UTILS_H_ diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index add300bdf3..05159e253d 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -36,6 +36,7 @@ #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" +#include "class_loader_utils.h" #include "compiler_callbacks.h" #include "dex/art_dex_file_loader.h" #include "dex/dex_file-inl.h" @@ -543,58 +544,23 @@ std::vector<const DexFile*> CommonRuntimeTestImpl::GetDexFiles(jobject jclass_lo std::vector<const DexFile*> CommonRuntimeTestImpl::GetDexFiles( ScopedObjectAccess& soa, Handle<mirror::ClassLoader> class_loader) { - std::vector<const DexFile*> ret; - DCHECK( (class_loader->GetClass() == soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)) || (class_loader->GetClass() == soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader))); - // The class loader is a PathClassLoader which inherits from BaseDexClassLoader. - // We need to get the DexPathList and loop through it. - ArtField* cookie_field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie); - ArtField* dex_file_field = - jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); - ObjPtr<mirror::Object> dex_path_list = - jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)-> - GetObject(class_loader.Get()); - if (dex_path_list != nullptr && dex_file_field!= nullptr && cookie_field != nullptr) { - // DexPathList has an array dexElements of Elements[] which each contain a dex file. - ObjPtr<mirror::Object> dex_elements_obj = - jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)-> - GetObject(dex_path_list); - // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look - // at the mCookie which is a DexFile vector. - if (dex_elements_obj != nullptr) { - StackHandleScope<1> hs(soa.Self()); - Handle<mirror::ObjectArray<mirror::Object>> dex_elements = - hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>()); - for (int32_t i = 0; i < dex_elements->GetLength(); ++i) { - ObjPtr<mirror::Object> element = dex_elements->GetWithoutChecks(i); - if (element == nullptr) { - // Should never happen, fall back to java code to throw a NPE. - break; - } - ObjPtr<mirror::Object> dex_file = dex_file_field->GetObject(element); - if (dex_file != nullptr) { - ObjPtr<mirror::LongArray> long_array = cookie_field->GetObject(dex_file)->AsLongArray(); - DCHECK(long_array != nullptr); - int32_t long_array_size = long_array->GetLength(); - for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) { - const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>( - long_array->GetWithoutChecks(j))); - if (cp_dex_file == nullptr) { - LOG(WARNING) << "Null DexFile"; - continue; - } - ret.push_back(cp_dex_file); - } - } - } - } - } - + std::vector<const DexFile*> ret; + VisitClassLoaderDexFiles(soa, + class_loader, + [&](const DexFile* cp_dex_file) { + if (cp_dex_file == nullptr) { + LOG(WARNING) << "Null DexFile"; + } else { + ret.push_back(cp_dex_file); + } + return true; + }); return ret; } diff --git a/runtime/debug_print.cc b/runtime/debug_print.cc new file mode 100644 index 0000000000..44a183669d --- /dev/null +++ b/runtime/debug_print.cc @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <sstream> + +#include "debug_print.h" + +#include "class_linker.h" +#include "class_table.h" +#include "class_loader_utils.h" +#include "dex/utf.h" +#include "gc/heap.h" +#include "gc/space/space-inl.h" +#include "mirror/class.h" +#include "mirror/class_loader.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-current-inl.h" +#include "well_known_classes.h" + +namespace art { + +std::string DescribeSpace(ObjPtr<mirror::Class> klass) { + std::ostringstream oss; + gc::Heap* heap = Runtime::Current()->GetHeap(); + gc::space::ContinuousSpace* cs = + heap->FindContinuousSpaceFromObject(klass.Ptr(), /* fail_ok */ true); + if (cs != nullptr) { + if (cs->IsImageSpace()) { + oss << "image;" << cs->GetName() << ";" << cs->AsImageSpace()->GetImageFilename(); + } else { + oss << "continuous;" << cs->GetName(); + } + } else { + gc::space::DiscontinuousSpace* ds = + heap->FindDiscontinuousSpaceFromObject(klass, /* fail_ok */ true); + if (ds != nullptr) { + oss << "discontinuous;" << ds->GetName(); + } else { + oss << "invalid"; + } + } + return oss.str(); +} + +std::string DescribeLoaders(ObjPtr<mirror::ClassLoader> loader, const char* class_descriptor) { + std::ostringstream oss; + uint32_t hash = ComputeModifiedUtf8Hash(class_descriptor); + ObjPtr<mirror::Class> path_class_loader = + WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_PathClassLoader); + ObjPtr<mirror::Class> dex_class_loader = + WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexClassLoader); + ObjPtr<mirror::Class> delegate_last_class_loader = + WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DelegateLastClassLoader); + + // Print the class loader chain. + bool found_class = false; + const char* loader_separator = ""; + if (loader == nullptr) { + oss << "BootClassLoader"; // This would be unexpected. + } + for (; loader != nullptr; loader = loader->GetParent()) { + oss << loader_separator << loader->GetClass()->PrettyDescriptor(); + loader_separator = ";"; + // If we didn't find the class yet, try to find it in the current class loader. + if (!found_class) { + ClassTable* table = Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(loader); + ObjPtr<mirror::Class> klass = + (table != nullptr) ? table->Lookup(class_descriptor, hash) : nullptr; + if (klass != nullptr) { + found_class = true; + oss << "[hit:" << DescribeSpace(klass) << "]"; + } + } + + // For PathClassLoader, DexClassLoader or DelegateLastClassLoader + // also dump the dex file locations. + if (loader->GetClass() == path_class_loader || + loader->GetClass() == dex_class_loader || + loader->GetClass() == delegate_last_class_loader) { + oss << "("; + ScopedObjectAccessUnchecked soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::ClassLoader> handle(hs.NewHandle(loader)); + const char* path_separator = ""; + VisitClassLoaderDexFiles(soa, + handle, + [&](const DexFile* dex_file) { + oss << path_separator << dex_file->GetLocation(); + path_separator = ":"; + return true; // Continue with the next DexFile. + }); + oss << ")"; + } + } + + return oss.str(); +} + +} // namespace art diff --git a/runtime/debug_print.h b/runtime/debug_print.h new file mode 100644 index 0000000000..479c36a02f --- /dev/null +++ b/runtime/debug_print.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_DEBUG_PRINT_H_ +#define ART_RUNTIME_DEBUG_PRINT_H_ + +#include "base/mutex.h" +#include "mirror/object.h" + +// Helper functions for printing extra information for certain hard to diagnose bugs. + +namespace art { + +std::string DescribeSpace(ObjPtr<mirror::Class> klass) + REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; +std::string DescribeLoaders(ObjPtr<mirror::ClassLoader> loader, const char* class_descriptor) + REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; + +} // namespace art + +#endif // ART_RUNTIME_DEBUG_PRINT_H_ diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 2eb3ab923c..66cec7113e 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -19,6 +19,7 @@ #include "base/enums.h" #include "callee_save_frame.h" #include "common_throws.h" +#include "debug_print.h" #include "debugger.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_types.h" @@ -1187,9 +1188,24 @@ static std::string DumpInstruction(ArtMethod* method, uint32_t dex_pc) } } +static void DumpB74410240ClassData(ObjPtr<mirror::Class> klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + std::string storage; + const char* descriptor = klass->GetDescriptor(&storage); + LOG(FATAL_WITHOUT_ABORT) << " " << DescribeLoaders(klass->GetClassLoader(), descriptor); + const OatDexFile* oat_dex_file = klass->GetDexFile().GetOatDexFile(); + if (oat_dex_file != nullptr) { + const OatFile* oat_file = oat_dex_file->GetOatFile(); + const char* dex2oat_cmdline = + oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey); + LOG(FATAL_WITHOUT_ABORT) << " OatFile: " << oat_file->GetLocation() + << "; " << (dex2oat_cmdline != nullptr ? dex2oat_cmdline : "<not recorded>"); + } +} + static void DumpB74410240DebugData(ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) { // Mimick the search for the caller and dump some data while doing so. - LOG(FATAL_WITHOUT_ABORT) << "Dumping debugging data for b/74410240."; + LOG(FATAL_WITHOUT_ABORT) << "Dumping debugging data, please attach a bugreport to b/74410240."; constexpr CalleeSaveType type = CalleeSaveType::kSaveRefsAndArgs; CHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(type)); @@ -1227,6 +1243,7 @@ static void DumpB74410240DebugData(ArtMethod** sp) REQUIRES_SHARED(Locks::mutato << " dex pc: " << dex_pc << " dex file: " << outer_method->GetDexFile()->GetLocation() << " class table: " << class_linker->ClassTableForClassLoader(outer_method->GetClassLoader()); + DumpB74410240ClassData(outer_method->GetDeclaringClass()); LOG(FATAL_WITHOUT_ABORT) << " instruction: " << DumpInstruction(outer_method, dex_pc); ArtMethod* caller = outer_method; @@ -1260,7 +1277,8 @@ static void DumpB74410240DebugData(ArtMethod** sp) REQUIRES_SHARED(Locks::mutato << " dex pc: " << dex_pc << " dex file: " << caller->GetDexFile()->GetLocation() << " class table: " - << class_linker->ClassTableForClassLoader(outer_method->GetClassLoader()); + << class_linker->ClassTableForClassLoader(caller->GetClassLoader()); + DumpB74410240ClassData(caller->GetDeclaringClass()); LOG(FATAL_WITHOUT_ABORT) << " instruction: " << DumpInstruction(caller, dex_pc); } } diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index f0b36a090a..02b4f5349d 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -16,13 +16,20 @@ #include "hidden_api.h" +#include <nativehelper/scoped_local_ref.h> + #include "base/dumpable.h" +#include "thread-current-inl.h" +#include "well_known_classes.h" namespace art { namespace hiddenapi { static inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { switch (value) { + case kNone: + LOG(FATAL) << "Internal access to hidden API should not be logged"; + UNREACHABLE(); case kReflection: os << "reflection"; break; @@ -42,12 +49,11 @@ static constexpr bool EnumsEqual(EnforcementPolicy policy, HiddenApiAccessFlags: // GetMemberAction-related static_asserts. static_assert( - EnumsEqual(EnforcementPolicy::kAllLists, HiddenApiAccessFlags::kLightGreylist) && EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) && EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist), "Mismatch between EnforcementPolicy and ApiList enums"); static_assert( - EnforcementPolicy::kAllLists < EnforcementPolicy::kDarkGreyAndBlackList && + EnforcementPolicy::kJustWarn < EnforcementPolicy::kDarkGreyAndBlackList && EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly, "EnforcementPolicy values ordering not correct"); @@ -111,62 +117,104 @@ void MemberSignature::WarnAboutAccess(AccessMethod access_method, } template<typename T> -bool ShouldBlockAccessToMemberImpl(T* member, Action action, AccessMethod access_method) { +Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) { + DCHECK_NE(action, kAllow); + // Get the signature, we need it later. MemberSignature member_signature(member); Runtime* runtime = Runtime::Current(); - if (action == kDeny) { - // If we were about to deny, check for an exemption first. - // Exempted APIs are treated as light grey list. + // Check for an exemption first. Exempted APIs are treated as white list. + // We only do this if we're about to deny, or if the app is debuggable. This is because: + // - we only print a warning for light greylist violations for debuggable apps + // - for non-debuggable apps, there is no distinction between light grey & whitelisted APIs. + // - we want to avoid the overhead of checking for exemptions for light greylisted APIs whenever + // possible. + if (action == kDeny || runtime->IsJavaDebuggable()) { if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) { - action = kAllowButWarn; + action = kAllow; // Avoid re-examining the exemption list next time. - // Note this results in the warning below showing "light greylist", which - // seems like what one would expect. Exemptions effectively add new members to - // the light greylist. + // Note this results in no warning for the member, which seems like what one would expect. + // Exemptions effectively adds new members to the whitelist. member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( - member->GetAccessFlags(), HiddenApiAccessFlags::kLightGreylist)); + member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist)); + return kAllow; } - } - // Print a log message with information about this class member access. - // We do this regardless of whether we block the access or not. - member_signature.WarnAboutAccess(access_method, - HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags())); + if (access_method != kNone) { + // Print a log message with information about this class member access. + // We do this if we're about to block access, or the app is debuggable. + member_signature.WarnAboutAccess(access_method, + HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags())); + } + } if (action == kDeny) { // Block access - return true; + return action; } // Allow access to this member but print a warning. DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast); - // Depending on a runtime flag, we might move the member into whitelist and - // skip the warning the next time the member is accessed. - if (runtime->ShouldDedupeHiddenApiWarnings()) { - member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( - member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist)); - } + if (access_method != kNone) { + // Depending on a runtime flag, we might move the member into whitelist and + // skip the warning the next time the member is accessed. + if (runtime->ShouldDedupeHiddenApiWarnings()) { + member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( + member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist)); + } - // If this action requires a UI warning, set the appropriate flag. - if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) { - runtime->SetPendingHiddenApiWarning(true); + // If this action requires a UI warning, set the appropriate flag. + if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) { + runtime->SetPendingHiddenApiWarning(true); + } } - return false; + return action; } // Need to instantiate this. -template bool ShouldBlockAccessToMemberImpl<ArtField>(ArtField* member, - Action action, - AccessMethod access_method); -template bool ShouldBlockAccessToMemberImpl<ArtMethod>(ArtMethod* member, - Action action, - AccessMethod access_method); - +template Action GetMemberActionImpl<ArtField>(ArtField* member, + Action action, + AccessMethod access_method); +template Action GetMemberActionImpl<ArtMethod>(ArtMethod* member, + Action action, + AccessMethod access_method); } // namespace detail + +template<typename T> +void NotifyHiddenApiListener(T* member) { + Runtime* runtime = Runtime::Current(); + if (!runtime->IsAotCompiler()) { + ScopedObjectAccessUnchecked soa(Thread::Current()); + + ScopedLocalRef<jobject> consumer_object(soa.Env(), + soa.Env()->GetStaticObjectField( + WellKnownClasses::dalvik_system_VMRuntime, + WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer)); + // If the consumer is non-null, we call back to it to let it know that we + // have encountered an API that's in one of our lists. + if (consumer_object != nullptr) { + detail::MemberSignature member_signature(member); + std::ostringstream member_signature_str; + member_signature.Dump(member_signature_str); + + ScopedLocalRef<jobject> signature_str( + soa.Env(), + soa.Env()->NewStringUTF(member_signature_str.str().c_str())); + + // Call through to Consumer.accept(String memberSignature); + soa.Env()->CallVoidMethod(consumer_object.get(), + WellKnownClasses::java_util_function_Consumer_accept, + signature_str.get()); + } + } +} + +template void NotifyHiddenApiListener<ArtMethod>(ArtMethod* member); +template void NotifyHiddenApiListener<ArtField>(ArtField* member); + } // namespace hiddenapi } // namespace art diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index cc6c146f00..d2c71a7d35 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -33,7 +33,7 @@ namespace hiddenapi { // frameworks/base/core/java/android/content/pm/ApplicationInfo.java enum class EnforcementPolicy { kNoChecks = 0, - kAllLists = 1, // ban anything but whitelist + kJustWarn = 1, // keep checks enabled, but allow everything (enables logging) kDarkGreyAndBlackList = 2, // ban dark grey & blacklist kBlacklistOnly = 3, // ban blacklist violations only kMax = kBlacklistOnly, @@ -53,12 +53,13 @@ enum Action { }; enum AccessMethod { + kNone, // internal test that does not correspond to an actual access by app kReflection, kJNI, kLinking, }; -inline Action GetMemberAction(uint32_t access_flags) { +inline Action GetActionFromAccessFlags(uint32_t access_flags) { EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); if (policy == EnforcementPolicy::kNoChecks) { // Exit early. Nothing to enforce. @@ -69,6 +70,11 @@ inline Action GetMemberAction(uint32_t access_flags) { if (api_list == HiddenApiAccessFlags::kWhitelist) { return kAllow; } + // if policy is "just warn", always warn. We returned above for whitelist APIs. + if (policy == EnforcementPolicy::kJustWarn) { + return kAllowButWarn; + } + DCHECK(policy >= EnforcementPolicy::kDarkGreyAndBlackList); // The logic below relies on equality of values in the enums EnforcementPolicy and // HiddenApiAccessFlags::ApiList, and their ordering. Assertions are in hidden_api.cc. if (static_cast<int>(policy) > static_cast<int>(api_list)) { @@ -108,9 +114,7 @@ class MemberSignature { }; template<typename T> -bool ShouldBlockAccessToMemberImpl(T* member, - Action action, - AccessMethod access_method) +Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_); // Returns true if the caller is either loaded by the boot strap class loader or comes from @@ -138,28 +142,28 @@ inline bool IsCallerInPlatformDex(ObjPtr<mirror::ClassLoader> caller_class_loade // return true if the caller is located in the platform. // This function might print warnings into the log if the member is hidden. template<typename T> -inline bool ShouldBlockAccessToMember(T* member, - Thread* self, - std::function<bool(Thread*)> fn_caller_in_platform, - AccessMethod access_method) +inline Action GetMemberAction(T* member, + Thread* self, + std::function<bool(Thread*)> fn_caller_in_platform, + AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(member != nullptr); - Action action = GetMemberAction(member->GetAccessFlags()); + Action action = GetActionFromAccessFlags(member->GetAccessFlags()); if (action == kAllow) { // Nothing to do. - return false; + return action; } // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access. // This can be *very* expensive. Save it for last. if (fn_caller_in_platform(self)) { // Caller in the platform. Exit. - return false; + return kAllow; } // Member is hidden and caller is not in the platform. - return detail::ShouldBlockAccessToMemberImpl(member, action, access_method); + return detail::GetMemberActionImpl(member, action, access_method); } inline bool IsCallerInPlatformDex(ObjPtr<mirror::Class> caller) @@ -172,18 +176,26 @@ inline bool IsCallerInPlatformDex(ObjPtr<mirror::Class> caller) // `caller_class_loader`. // This function might print warnings into the log if the member is hidden. template<typename T> -inline bool ShouldBlockAccessToMember(T* member, - ObjPtr<mirror::ClassLoader> caller_class_loader, - ObjPtr<mirror::DexCache> caller_dex_cache, - AccessMethod access_method) +inline Action GetMemberAction(T* member, + ObjPtr<mirror::ClassLoader> caller_class_loader, + ObjPtr<mirror::DexCache> caller_dex_cache, + AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { bool caller_in_platform = detail::IsCallerInPlatformDex(caller_class_loader, caller_dex_cache); - return ShouldBlockAccessToMember(member, - /* thread */ nullptr, - [caller_in_platform] (Thread*) { return caller_in_platform; }, - access_method); + return GetMemberAction(member, + /* thread */ nullptr, + [caller_in_platform] (Thread*) { return caller_in_platform; }, + access_method); } +// Calls back into managed code to notify VMRuntime.nonSdkApiUsageConsumer that +// |member| was accessed. This is usually called when an API is on the black, +// dark grey or light grey lists. Given that the callback can execute arbitrary +// code, a call to this method can result in thread suspension. +template<typename T> void NotifyHiddenApiListener(T* member) + REQUIRES_SHARED(Locks::mutator_lock_); + + } // namespace hiddenapi } // namespace art diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc index 5a31dd4972..65d6363bfd 100644 --- a/runtime/hidden_api_test.cc +++ b/runtime/hidden_api_test.cc @@ -22,6 +22,7 @@ namespace art { using hiddenapi::detail::MemberSignature; +using hiddenapi::GetActionFromAccessFlags; class HiddenApiTest : public CommonRuntimeTest { protected: @@ -84,6 +85,39 @@ class HiddenApiTest : public CommonRuntimeTest { ArtMethod* class3_method1_i_; }; +TEST_F(HiddenApiTest, CheckGetActionFromRuntimeFlags) { + uint32_t whitelist = HiddenApiAccessFlags::EncodeForRuntime(0, HiddenApiAccessFlags::kWhitelist); + uint32_t lightgreylist = + HiddenApiAccessFlags::EncodeForRuntime(0, HiddenApiAccessFlags::kLightGreylist); + uint32_t darkgreylist = + HiddenApiAccessFlags::EncodeForRuntime(0, HiddenApiAccessFlags::kDarkGreylist); + uint32_t blacklist = HiddenApiAccessFlags::EncodeForRuntime(0, HiddenApiAccessFlags::kBlacklist); + + runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks); + ASSERT_EQ(GetActionFromAccessFlags(whitelist), hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(lightgreylist), hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(darkgreylist), hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(blacklist), hiddenapi::kAllow); + + runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kJustWarn); + ASSERT_EQ(GetActionFromAccessFlags(whitelist), hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(lightgreylist), hiddenapi::kAllowButWarn); + ASSERT_EQ(GetActionFromAccessFlags(darkgreylist), hiddenapi::kAllowButWarn); + ASSERT_EQ(GetActionFromAccessFlags(blacklist), hiddenapi::kAllowButWarn); + + runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDarkGreyAndBlackList); + ASSERT_EQ(GetActionFromAccessFlags(whitelist), hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(lightgreylist), hiddenapi::kAllowButWarn); + ASSERT_EQ(GetActionFromAccessFlags(darkgreylist), hiddenapi::kDeny); + ASSERT_EQ(GetActionFromAccessFlags(blacklist), hiddenapi::kDeny); + + runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly); + ASSERT_EQ(GetActionFromAccessFlags(whitelist), hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(lightgreylist), hiddenapi::kAllowButWarn); + ASSERT_EQ(GetActionFromAccessFlags(darkgreylist), hiddenapi::kAllowButWarnAndToast); + ASSERT_EQ(GetActionFromAccessFlags(blacklist), hiddenapi::kDeny); +} + TEST_F(HiddenApiTest, CheckMembersRead) { ASSERT_NE(nullptr, class1_field1_); ASSERT_NE(nullptr, class1_field12_); diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 4a2dd3bc3f..6e920363e8 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -181,11 +181,13 @@ static mirror::String* GetClassName(Thread* self, ShadowFrame* shadow_frame, siz template<typename T> static ALWAYS_INLINE bool ShouldBlockAccessToMember(T* member, ShadowFrame* frame) REQUIRES_SHARED(Locks::mutator_lock_) { - return hiddenapi::ShouldBlockAccessToMember( + // All uses in this file are from reflection + constexpr hiddenapi::AccessMethod access_method = hiddenapi::kReflection; + return hiddenapi::GetMemberAction( member, frame->GetMethod()->GetDeclaringClass()->GetClassLoader(), frame->GetMethod()->GetDeclaringClass()->GetDexCache(), - hiddenapi::kReflection); // all uses in this file are from reflection + access_method) == hiddenapi::kDeny; } void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self, diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index f309581735..9dbcded867 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -87,8 +87,13 @@ static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_l template<typename T> ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - return hiddenapi::ShouldBlockAccessToMember( + hiddenapi::Action action = hiddenapi::GetMemberAction( member, self, IsCallerInPlatformDex, hiddenapi::kJNI); + if (action != hiddenapi::kAllow) { + hiddenapi::NotifyHiddenApiListener(member); + } + + return action == hiddenapi::kDeny; } // Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index ad05856eaf..a8b203bff2 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -98,8 +98,13 @@ ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self) template<typename T> ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - return hiddenapi::ShouldBlockAccessToMember( + hiddenapi::Action action = hiddenapi::GetMemberAction( member, self, IsCallerInPlatformDex, hiddenapi::kReflection); + if (action != hiddenapi::kAllow) { + hiddenapi::NotifyHiddenApiListener(member); + } + + return action == hiddenapi::kDeny; } // Returns true if a class member should be discoverable with reflection given @@ -113,7 +118,8 @@ ALWAYS_INLINE static bool IsDiscoverable(bool public_only, return false; } - if (enforce_hidden_api && hiddenapi::GetMemberAction(access_flags) == hiddenapi::kDeny) { + if (enforce_hidden_api && + hiddenapi::GetActionFromAccessFlags(access_flags) == hiddenapi::kDeny) { return false; } @@ -433,12 +439,14 @@ static jobject Class_getPublicFieldRecursive(JNIEnv* env, jobject javaThis, jstr return nullptr; } - mirror::Field* field = GetPublicFieldRecursive( - soa.Self(), DecodeClass(soa, javaThis), name_string); - if (field == nullptr || ShouldBlockAccessToMember(field->GetArtField(), soa.Self())) { + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::Field> field = hs.NewHandle(GetPublicFieldRecursive( + soa.Self(), DecodeClass(soa, javaThis), name_string)); + if (field.Get() == nullptr || + ShouldBlockAccessToMember(field->GetArtField(), soa.Self())) { return nullptr; } - return soa.AddLocalReference<jobject>(field); + return soa.AddLocalReference<jobject>(field.Get()); } static jobject Class_getDeclaredField(JNIEnv* env, jobject javaThis, jstring name) { @@ -477,15 +485,17 @@ static jobject Class_getDeclaredConstructorInternal( ScopedFastNativeObjectAccess soa(env); DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); DCHECK(!Runtime::Current()->IsActiveTransaction()); - ObjPtr<mirror::Constructor> result = + + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::Constructor> result = hs.NewHandle( mirror::Class::GetDeclaredConstructorInternal<kRuntimePointerSize, false>( soa.Self(), DecodeClass(soa, javaThis), - soa.Decode<mirror::ObjectArray<mirror::Class>>(args)); + soa.Decode<mirror::ObjectArray<mirror::Class>>(args))); if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) { return nullptr; } - return soa.AddLocalReference<jobject>(result); + return soa.AddLocalReference<jobject>(result.Get()); } static ALWAYS_INLINE inline bool MethodMatchesConstructor( @@ -535,18 +545,19 @@ static jobjectArray Class_getDeclaredConstructorsInternal( static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis, jstring name, jobjectArray args) { ScopedFastNativeObjectAccess soa(env); + StackHandleScope<1> hs(soa.Self()); DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); DCHECK(!Runtime::Current()->IsActiveTransaction()); - ObjPtr<mirror::Method> result = + Handle<mirror::Method> result = hs.NewHandle( mirror::Class::GetDeclaredMethodInternal<kRuntimePointerSize, false>( soa.Self(), DecodeClass(soa, javaThis), soa.Decode<mirror::String>(name), - soa.Decode<mirror::ObjectArray<mirror::Class>>(args)); + soa.Decode<mirror::ObjectArray<mirror::Class>>(args))); if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) { return nullptr; } - return soa.AddLocalReference<jobject>(result); + return soa.AddLocalReference<jobject>(result.Get()); } static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaThis, diff --git a/runtime/runtime.h b/runtime/runtime.h index 03f17bc04a..c14593749e 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -1004,7 +1004,8 @@ class Runtime { // Whether access checks on hidden API should be performed. hiddenapi::EnforcementPolicy hidden_api_policy_; - // List of signature prefixes of methods that have been removed from the blacklist + // List of signature prefixes of methods that have been removed from the blacklist, and treated + // as if whitelisted. std::vector<std::string> hidden_api_exemptions_; // Whether the application has used an API which is not restricted but we diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 0206d2c1e5..dfb98b6083 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -3801,16 +3801,8 @@ ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess( must_fail = true; // Try to find the method also with the other type for better error reporting below // but do not store such bogus lookup result in the DexCache or VerifierDeps. - if (klass->IsInterface()) { - // NB This is normally not really allowed but we want to get any static or private object - // methods for error message purposes. This will never be returned. - // TODO We might want to change the verifier to not require this. - res_method = klass->FindClassMethod(dex_cache_.Get(), dex_method_idx, pointer_size); - } else { - // If there was an interface method with the same signature, - // we would have found it also in the "copied" methods. - DCHECK(klass->FindInterfaceMethod(dex_cache_.Get(), dex_method_idx, pointer_size) == nullptr); - } + res_method = class_linker->FindIncompatibleMethod( + klass, dex_cache_.Get(), class_loader_.Get(), dex_method_idx); } if (res_method == nullptr) { diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index bf36ccf0fa..742e713774 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -77,6 +77,7 @@ jclass WellKnownClasses::java_nio_ByteBuffer; jclass WellKnownClasses::java_nio_DirectByteBuffer; jclass WellKnownClasses::java_util_ArrayList; jclass WellKnownClasses::java_util_Collections; +jclass WellKnownClasses::java_util_function_Consumer; jclass WellKnownClasses::libcore_reflect_AnnotationFactory; jclass WellKnownClasses::libcore_reflect_AnnotationMember; jclass WellKnownClasses::libcore_util_EmptyArray; @@ -115,6 +116,7 @@ jmethodID WellKnownClasses::java_lang_Thread_run; jmethodID WellKnownClasses::java_lang_ThreadGroup_add; jmethodID WellKnownClasses::java_lang_ThreadGroup_removeThread; jmethodID WellKnownClasses::java_nio_DirectByteBuffer_init; +jmethodID WellKnownClasses::java_util_function_Consumer_accept; jmethodID WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation; jmethodID WellKnownClasses::libcore_reflect_AnnotationMember_init; jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast; @@ -125,6 +127,7 @@ jfieldID WellKnownClasses::dalvik_system_DexFile_fileName; jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList; jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements; jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile; +jfieldID WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer; jfieldID WellKnownClasses::java_lang_Thread_daemon; jfieldID WellKnownClasses::java_lang_Thread_group; jfieldID WellKnownClasses::java_lang_Thread_lock; @@ -349,6 +352,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_nio_DirectByteBuffer = CacheClass(env, "java/nio/DirectByteBuffer"); java_util_ArrayList = CacheClass(env, "java/util/ArrayList"); java_util_Collections = CacheClass(env, "java/util/Collections"); + java_util_function_Consumer = CacheClass(env, "java/util/function/Consumer"); libcore_reflect_AnnotationFactory = CacheClass(env, "libcore/reflect/AnnotationFactory"); libcore_reflect_AnnotationMember = CacheClass(env, "libcore/reflect/AnnotationMember"); libcore_util_EmptyArray = CacheClass(env, "libcore/util/EmptyArray"); @@ -379,6 +383,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_ThreadGroup_add = CacheMethod(env, java_lang_ThreadGroup, false, "add", "(Ljava/lang/Thread;)V"); java_lang_ThreadGroup_removeThread = CacheMethod(env, java_lang_ThreadGroup, false, "threadTerminated", "(Ljava/lang/Thread;)V"); java_nio_DirectByteBuffer_init = CacheMethod(env, java_nio_DirectByteBuffer, false, "<init>", "(JI)V"); + java_util_function_Consumer_accept = CacheMethod(env, java_util_function_Consumer, false, "accept", "(Ljava/lang/Object;)V"); libcore_reflect_AnnotationFactory_createAnnotation = CacheMethod(env, libcore_reflect_AnnotationFactory, true, "createAnnotation", "(Ljava/lang/Class;[Llibcore/reflect/AnnotationMember;)Ljava/lang/annotation/Annotation;"); libcore_reflect_AnnotationMember_init = CacheMethod(env, libcore_reflect_AnnotationMember, false, "<init>", "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/reflect/Method;)V"); org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "broadcast", "(I)V"); @@ -389,6 +394,7 @@ void WellKnownClasses::Init(JNIEnv* env) { dalvik_system_DexFile_fileName = CacheField(env, dalvik_system_DexFile, false, "mFileName", "Ljava/lang/String;"); dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;"); dalvik_system_DexPathList__Element_dexFile = CacheField(env, dalvik_system_DexPathList__Element, false, "dexFile", "Ldalvik/system/DexFile;"); + dalvik_system_VMRuntime_nonSdkApiUsageConsumer = CacheField(env, dalvik_system_VMRuntime, true, "nonSdkApiUsageConsumer", "Ljava/util/function/Consumer;"); java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z"); java_lang_Thread_group = CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;"); java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index d5d7033132..7b1a2943d2 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -85,6 +85,7 @@ struct WellKnownClasses { static jclass java_lang_Throwable; static jclass java_util_ArrayList; static jclass java_util_Collections; + static jclass java_util_function_Consumer; static jclass java_nio_ByteBuffer; static jclass java_nio_DirectByteBuffer; static jclass libcore_reflect_AnnotationFactory; @@ -125,6 +126,7 @@ struct WellKnownClasses { static jmethodID java_lang_ThreadGroup_add; static jmethodID java_lang_ThreadGroup_removeThread; static jmethodID java_nio_DirectByteBuffer_init; + static jmethodID java_util_function_Consumer_accept; static jmethodID libcore_reflect_AnnotationFactory_createAnnotation; static jmethodID libcore_reflect_AnnotationMember_init; static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_broadcast; @@ -135,6 +137,7 @@ struct WellKnownClasses { static jfieldID dalvik_system_DexFile_fileName; static jfieldID dalvik_system_DexPathList_dexElements; static jfieldID dalvik_system_DexPathList__Element_dexFile; + static jfieldID dalvik_system_VMRuntime_nonSdkApiUsageConsumer; static jfieldID java_lang_reflect_Executable_artMethod; static jfieldID java_lang_reflect_Proxy_h; static jfieldID java_lang_Thread_daemon; |