diff options
-rw-r--r-- | runtime/class_linker.cc | 20 | ||||
-rw-r--r-- | runtime/class_linker.h | 5 | ||||
-rw-r--r-- | runtime/vdex_file.cc | 141 | ||||
-rw-r--r-- | runtime/vdex_file.h | 10 | ||||
-rw-r--r-- | runtime/verifier/verifier_deps.cc | 11 | ||||
-rw-r--r-- | runtime/verifier/verifier_deps.h | 4 | ||||
-rw-r--r-- | test/692-vdex-inmem-loader/vdex_inmem_loader.cc | 2 | ||||
-rw-r--r-- | test/800-smali/jni.cc | 7 |
8 files changed, 183 insertions, 17 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 361749073a..6641f80299 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -145,6 +145,7 @@ #include "thread_list.h" #include "trace.h" #include "transaction.h" +#include "vdex_file.h" #include "verifier/class_verifier.h" #include "well_known_classes.h" @@ -4623,7 +4624,7 @@ verifier::FailureKind ClassLinker::VerifyClass( // Try to use verification information from the oat file, otherwise do runtime verification. const DexFile& dex_file = *klass->GetDexCache()->GetDexFile(); ClassStatus oat_file_class_status(ClassStatus::kNotReady); - bool preverified = VerifyClassUsingOatFile(dex_file, klass.Get(), oat_file_class_status); + bool preverified = VerifyClassUsingOatFile(self, dex_file, klass, oat_file_class_status); VLOG(class_linker) << "Class preverified status for class " << klass->PrettyDescriptor() @@ -4738,8 +4739,9 @@ verifier::FailureKind ClassLinker::PerformClassVerification(Thread* self, error_msg); } -bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, - ObjPtr<mirror::Class> klass, +bool ClassLinker::VerifyClassUsingOatFile(Thread* self, + const DexFile& dex_file, + Handle<mirror::Class> klass, ClassStatus& oat_file_class_status) { // If we're compiling, we can only verify the class using the oat file if // we are not compiling the image or if the class we're verifying is not part of @@ -4748,7 +4750,7 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, if (Runtime::Current()->IsAotCompiler()) { CompilerCallbacks* callbacks = Runtime::Current()->GetCompilerCallbacks(); // We are compiling an app (not the image). - if (!callbacks->CanUseOatStatusForVerification(klass.Ptr())) { + if (!callbacks->CanUseOatStatusForVerification(klass.Get())) { return false; } } @@ -4769,6 +4771,16 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, // check the class status to ensure we run with access checks. return true; } + + // Check the class status with the vdex file. + const OatFile* oat_file = oat_dex_file->GetOatFile(); + if (oat_file != nullptr) { + oat_file_class_status = oat_file->GetVdexFile()->ComputeClassStatus(self, klass); + if (oat_file_class_status >= ClassStatus::kVerifiedNeedsAccessChecks) { + return true; + } + } + // If we only verified a subset of the classes at compile time, we can end up with classes that // were resolved by the verifier. if (oat_file_class_status == ClassStatus::kResolved) { diff --git a/runtime/class_linker.h b/runtime/class_linker.h index f9becf97b2..95dc6cfaff 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -557,8 +557,9 @@ class ClassLinker { verifier::HardFailLogMode log_level = verifier::HardFailLogMode::kLogNone) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); - bool VerifyClassUsingOatFile(const DexFile& dex_file, - ObjPtr<mirror::Class> klass, + bool VerifyClassUsingOatFile(Thread* self, + const DexFile& dex_file, + Handle<mirror::Class> klass, ClassStatus& oat_file_class_status) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index d67a968ae8..70fde27105 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -37,7 +37,9 @@ #include "dex_to_dex_decompiler.h" #include "gc/heap.h" #include "gc/space/image_space.h" +#include "mirror/class-inl.h" #include "quicken_info.h" +#include "handle_scope-inl.h" #include "runtime.h" #include "verifier/verifier_deps.h" @@ -506,4 +508,143 @@ bool VdexFile::MatchesClassLoaderContext(const ClassLoaderContext& context) cons } } +static ObjPtr<mirror::Class> FindClassAndClearException(ClassLinker* class_linker, + Thread* self, + const char* name, + Handle<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr<mirror::Class> result = class_linker->FindClass(self, name, class_loader); + if (result == nullptr) { + DCHECK(self->IsExceptionPending()); + self->ClearException(); + } + return result; +} + +static const char* GetStringFromId(const DexFile& dex_file, + dex::StringIndex string_id, + uint32_t number_of_extra_strings, + const uint32_t* extra_strings_offsets, + const uint8_t* verifier_deps) { + uint32_t num_ids_in_dex = dex_file.NumStringIds(); + if (string_id.index_ < num_ids_in_dex) { + return dex_file.StringDataByIdx(string_id); + } else { + CHECK_LT(string_id.index_ - num_ids_in_dex, number_of_extra_strings); + uint32_t offset = extra_strings_offsets[string_id.index_ - num_ids_in_dex]; + return reinterpret_cast<const char*>(verifier_deps) + offset; + } +} + +// Returns an array of offsets where the assignability checks for each class +// definition are stored. +static const uint32_t* GetDexFileClassDefs(const uint8_t* verifier_deps, uint32_t index) { + uint32_t dex_file_offset = reinterpret_cast<const uint32_t*>(verifier_deps)[index]; + return reinterpret_cast<const uint32_t*>(verifier_deps + dex_file_offset); +} + +// Returns an array of offsets where extra strings are stored. +static const uint32_t* GetExtraStringsOffsets(const DexFile& dex_file, + const uint8_t* verifier_deps, + const uint32_t* dex_file_class_defs, + /*out*/ uint32_t* number_of_extra_strings) { + // The information for strings is right after dex_file_class_defs, 4-byte + // aligned + uint32_t end_of_assignability_types = dex_file_class_defs[dex_file.NumClassDefs()]; + const uint8_t* strings_data_start = + AlignUp(verifier_deps + end_of_assignability_types, sizeof(uint32_t)); + // First entry is the number of extra strings for this dex file. + *number_of_extra_strings = *reinterpret_cast<const uint32_t*>(strings_data_start); + // Then an array of offsets in `verifier_deps` for the extra strings. + return reinterpret_cast<const uint32_t*>(strings_data_start + sizeof(uint32_t)); +} + +ClassStatus VdexFile::ComputeClassStatus(Thread* self, Handle<mirror::Class> cls) const { + const DexFile& dex_file = cls->GetDexFile(); + uint16_t class_def_index = cls->GetDexClassDefIndex(); + + // Find which dex file index from within the vdex file. + uint32_t index = 0; + for (; index < GetVerifierDepsHeader().GetNumberOfDexFiles(); ++index) { + if (dex_file.GetLocationChecksum() == GetLocationChecksum(index)) { + break; + } + } + DCHECK_NE(index, GetVerifierDepsHeader().GetNumberOfDexFiles()); + + const uint8_t* verifier_deps = GetVerifierDepsStart(); + const uint32_t* dex_file_class_defs = GetDexFileClassDefs(verifier_deps, index); + + // Fetch type checks offsets. + uint32_t class_def_offset = dex_file_class_defs[class_def_index]; + if (class_def_offset == verifier::VerifierDeps::kNotVerifiedMarker) { + // Return a status that needs re-verification. + return ClassStatus::kResolved; + } + // End offset for this class's type checks. We know there is one and the loop + // will terminate. + uint32_t end_offset = verifier::VerifierDeps::kNotVerifiedMarker; + for (uint32_t i = class_def_index + 1; i < dex_file.NumClassDefs() + 1; ++i) { + end_offset = dex_file_class_defs[i]; + if (end_offset != verifier::VerifierDeps::kNotVerifiedMarker) { + break; + } + } + DCHECK_NE(end_offset, verifier::VerifierDeps::kNotVerifiedMarker); + + uint32_t number_of_extra_strings = 0; + // Offset where extra strings are stored. + const uint32_t* extra_strings_offsets = GetExtraStringsOffsets(dex_file, + verifier_deps, + dex_file_class_defs, + &number_of_extra_strings); + + // Loop over and perform each assignability check. + StackHandleScope<3> hs(self); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(cls->GetClassLoader())); + MutableHandle<mirror::Class> source(hs.NewHandle<mirror::Class>(nullptr)); + MutableHandle<mirror::Class> destination(hs.NewHandle<mirror::Class>(nullptr)); + + const uint8_t* cursor = verifier_deps + class_def_offset; + const uint8_t* end = verifier_deps + end_offset; + while (cursor < end) { + uint32_t destination_index; + uint32_t source_index; + if (UNLIKELY(!DecodeUnsignedLeb128Checked(&cursor, end, &destination_index) || + !DecodeUnsignedLeb128Checked(&cursor, end, &source_index))) { + // Error parsing the data, just return that we are not verified. + return ClassStatus::kResolved; + } + const char* destination_desc = GetStringFromId(dex_file, + dex::StringIndex(destination_index), + number_of_extra_strings, + extra_strings_offsets, + verifier_deps); + destination.Assign( + FindClassAndClearException(class_linker, self, destination_desc, class_loader)); + + const char* source_desc = GetStringFromId(dex_file, + dex::StringIndex(source_index), + number_of_extra_strings, + extra_strings_offsets, + verifier_deps); + source.Assign(FindClassAndClearException(class_linker, self, source_desc, class_loader)); + + if (destination == nullptr || source == nullptr) { + // The interpreter / compiler can handle a missing class. + continue; + } + + DCHECK(destination->IsResolved() && source->IsResolved()); + if (!destination->IsAssignableFrom(source.Get())) { + // An implicit assignability check is failing in the code, return that the + // class is not verified. + return ClassStatus::kResolved; + } + } + + return ClassStatus::kVerifiedNeedsAccessChecks; +} + } // namespace art diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index c8823ab131..97655f6599 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -24,13 +24,20 @@ #include "base/macros.h" #include "base/mem_map.h" #include "base/os.h" +#include "class_status.h" #include "dex/compact_offset_table.h" #include "dex/dex_file.h" #include "quicken_info.h" +#include "handle.h" namespace art { class ClassLoaderContext; +class Thread; + +namespace mirror { +class Class; +} namespace verifier { class VerifierDeps; @@ -374,6 +381,9 @@ class VdexFile { // dequickening. void AllowWriting(bool value) const; + ClassStatus ComputeClassStatus(Thread* self, Handle<mirror::Class> cls) const + REQUIRES_SHARED(Locks::mutator_lock_); + private: uint32_t GetQuickeningInfoTableOffset(const uint8_t* source_dex_begin) const; diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc index 0fdc8c6302..7a27a0152a 100644 --- a/runtime/verifier/verifier_deps.cc +++ b/runtime/verifier/verifier_deps.cc @@ -385,10 +385,6 @@ static inline bool DecodeTuple(const uint8_t** in, const uint8_t* end, std::tupl return true; } -// Marker to know whether a class is verified. A non-verified class will have -// this marker as its offset entry in the encoded data. -static uint32_t constexpr kNotVerifiedMarker = std::numeric_limits<uint32_t>::max(); - static void SetUint32InUint8Array(std::vector<uint8_t>* out, uint32_t uint8_offset, uint32_t uint32_offset, @@ -414,7 +410,7 @@ static void EncodeSetVector(std::vector<uint8_t>* out, EncodeTuple(out, entry); } } else { - SetUint32InUint8Array(out, offsets_index, class_def_index, kNotVerifiedMarker); + SetUint32InUint8Array(out, offsets_index, class_def_index, VerifierDeps::kNotVerifiedMarker); } class_def_index++; } @@ -435,7 +431,7 @@ static bool DecodeSetVector(const uint8_t** cursor, *cursor += (num_class_defs + 1) * sizeof(uint32_t); for (uint32_t i = 0; i < num_class_defs; ++i) { uint32_t offset = offsets[i]; - if (offset == kNotVerifiedMarker) { + if (offset == VerifierDeps::kNotVerifiedMarker) { (*verified_classes)[i] = false; continue; } @@ -446,7 +442,8 @@ static bool DecodeSetVector(const uint8_t** cursor, // Find the offset of the next entry. This will tell us where to stop when // reading the checks. Note that the last entry in the `offsets` array points // to the end of the assignability types data, so the loop will terminate correctly. - while (next_valid_offset_index <= i || offsets[next_valid_offset_index] == kNotVerifiedMarker) { + while (next_valid_offset_index <= i || + offsets[next_valid_offset_index] == VerifierDeps::kNotVerifiedMarker) { next_valid_offset_index++; } const uint8_t* set_end = start + offsets[next_valid_offset_index]; diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h index d2a564e941..40ece5fee4 100644 --- a/runtime/verifier/verifier_deps.h +++ b/runtime/verifier/verifier_deps.h @@ -62,6 +62,10 @@ class VerifierDeps { public: explicit VerifierDeps(const std::vector<const DexFile*>& dex_files, bool output_only = true); + // Marker to know whether a class is verified. A non-verified class will have + // this marker as its offset entry in the encoded data. + static uint32_t constexpr kNotVerifiedMarker = std::numeric_limits<uint32_t>::max(); + // Fill dependencies from stored data. Returns true on success, false on failure. bool ParseStoredData(const std::vector<const DexFile*>& dex_files, ArrayRef<const uint8_t> data); diff --git a/test/692-vdex-inmem-loader/vdex_inmem_loader.cc b/test/692-vdex-inmem-loader/vdex_inmem_loader.cc index a10e2e7054..1d78f8dc68 100644 --- a/test/692-vdex-inmem-loader/vdex_inmem_loader.cc +++ b/test/692-vdex-inmem-loader/vdex_inmem_loader.cc @@ -158,7 +158,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_areClassesPreverified(JNIEnv*, ClassStatus oat_file_class_status(ClassStatus::kNotReady); bool is_preverified = class_linker->VerifyClassUsingOatFile( - *dex_file, h_class.Get(), oat_file_class_status); + soa.Self(), *dex_file, h_class, oat_file_class_status); if (is_first) { all_preverified = is_preverified; diff --git a/test/800-smali/jni.cc b/test/800-smali/jni.cc index bf9e88ab45..15830a3cf2 100644 --- a/test/800-smali/jni.cc +++ b/test/800-smali/jni.cc @@ -29,11 +29,12 @@ namespace { extern "C" JNIEXPORT jboolean JNICALL Java_Main_isAotVerified(JNIEnv* env, jclass, jclass cls) { ScopedObjectAccess soa(env); Runtime* rt = Runtime::Current(); - - ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::Class> klass = hs.NewHandle(soa.Decode<mirror::Class>(cls)); const DexFile& dex_file = *klass->GetDexCache()->GetDexFile(); ClassStatus oat_file_class_status(ClassStatus::kNotReady); - bool ret = rt->GetClassLinker()->VerifyClassUsingOatFile(dex_file, klass, oat_file_class_status); + bool ret = rt->GetClassLinker()->VerifyClassUsingOatFile( + soa.Self(), dex_file, klass, oat_file_class_status); return ret; } |