Add VerifierDeps::Verify.
Method verifies that the recorded dependencies still hold.
test: verifier_deps_test.cc
bug: 30937355
Change-Id: I137f77739355141e46994b4cb6043dbbc9353637
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 3a53998..8d2a0e7 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -399,6 +399,18 @@
has_unverified_classes;
}
+ static std::set<VerifierDeps::MethodResolution>* GetMethods(
+ VerifierDeps::DexFileDeps* deps, MethodResolutionKind resolution_kind) {
+ if (resolution_kind == kDirectMethodResolution) {
+ return &deps->direct_methods_;
+ } else if (resolution_kind == kVirtualMethodResolution) {
+ return &deps->virtual_methods_;
+ } else {
+ DCHECK_EQ(resolution_kind, kInterfaceMethodResolution);
+ return &deps->interface_methods_;
+ }
+ }
+
std::unique_ptr<verifier::VerifierDeps> verifier_deps_;
std::vector<const DexFile*> dex_files_;
const DexFile* primary_dex_file_;
@@ -1121,5 +1133,311 @@
ASSERT_TRUE(HasUnverifiedClass("LMyClassWithNoSuperButFailures;"));
}
+// Returns the next resolution kind in the enum.
+static MethodResolutionKind GetNextResolutionKind(MethodResolutionKind resolution_kind) {
+ if (resolution_kind == kDirectMethodResolution) {
+ return kVirtualMethodResolution;
+ } else if (resolution_kind == kVirtualMethodResolution) {
+ return kInterfaceMethodResolution;
+ } else {
+ DCHECK_EQ(resolution_kind, kInterfaceMethodResolution);
+ return kDirectMethodResolution;
+ }
+}
+
+TEST_F(VerifierDepsTest, VerifyDeps) {
+ VerifyDexFile();
+
+ ASSERT_EQ(1u, NumberOfCompiledDexFiles());
+ ASSERT_TRUE(HasEachKindOfRecord());
+
+ // When validating, we create a new class loader, as
+ // the existing `class_loader_` may contain erroneous classes,
+ // that ClassLinker::FindClass won't return.
+
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<1> hs(soa.Self());
+ MutableHandle<mirror::ClassLoader> new_class_loader(hs.NewHandle<mirror::ClassLoader>(nullptr));
+ {
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_TRUE(verifier_deps_->Verify(new_class_loader, soa.Self()));
+ }
+
+ std::vector<uint8_t> buffer;
+ verifier_deps_->Encode(dex_files_, &buffer);
+ ASSERT_FALSE(buffer.empty());
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_TRUE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ // Fiddle with the dependencies to make sure we catch any change and fail to verify.
+
+ {
+ // Mess up with the assignable_types.
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ deps->assignable_types_.insert(*deps->unassignable_types_.begin());
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ // Mess up with the unassignable_types.
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ deps->unassignable_types_.insert(*deps->assignable_types_.begin());
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ // Mess up with classes.
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->classes_) {
+ if (entry.IsResolved()) {
+ deps->classes_.insert(VerifierDeps::ClassResolution(
+ entry.GetDexTypeIndex(), VerifierDeps::kUnresolvedMarker));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->classes_) {
+ if (!entry.IsResolved()) {
+ deps->classes_.insert(VerifierDeps::ClassResolution(
+ entry.GetDexTypeIndex(), VerifierDeps::kUnresolvedMarker - 1));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->classes_) {
+ if (entry.IsResolved()) {
+ deps->classes_.insert(VerifierDeps::ClassResolution(
+ entry.GetDexTypeIndex(), entry.GetAccessFlags() - 1));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ // Mess up with fields.
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->fields_) {
+ if (entry.IsResolved()) {
+ deps->fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
+ VerifierDeps::kUnresolvedMarker,
+ entry.GetDeclaringClassIndex()));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->fields_) {
+ if (!entry.IsResolved()) {
+ deps->fields_.insert(VerifierDeps::FieldResolution(0 /* we know there is a field there */,
+ VerifierDeps::kUnresolvedMarker - 1,
+ 0 /* we know there is a class there */));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->fields_) {
+ if (entry.IsResolved()) {
+ deps->fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
+ entry.GetAccessFlags() - 1,
+ entry.GetDeclaringClassIndex()));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->fields_) {
+ static constexpr uint32_t kNewTypeIndex = 0;
+ if (entry.GetDeclaringClassIndex() != kNewTypeIndex) {
+ deps->fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
+ entry.GetAccessFlags(),
+ kNewTypeIndex));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ // Mess up with methods.
+ for (MethodResolutionKind resolution_kind :
+ { kDirectMethodResolution, kVirtualMethodResolution, kInterfaceMethodResolution }) {
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
+ for (const auto& entry : *methods) {
+ if (entry.IsResolved()) {
+ methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+ VerifierDeps::kUnresolvedMarker,
+ entry.GetDeclaringClassIndex()));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
+ for (const auto& entry : *methods) {
+ if (!entry.IsResolved()) {
+ methods->insert(VerifierDeps::MethodResolution(0 /* we know there is a method there */,
+ VerifierDeps::kUnresolvedMarker - 1,
+ 0 /* we know there is a class there */));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
+ for (const auto& entry : *methods) {
+ if (entry.IsResolved()) {
+ methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+ entry.GetAccessFlags() - 1,
+ entry.GetDeclaringClassIndex()));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
+ for (const auto& entry : *methods) {
+ static constexpr uint32_t kNewTypeIndex = 0;
+ if (entry.IsResolved() && entry.GetDeclaringClassIndex() != kNewTypeIndex) {
+ methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+ entry.GetAccessFlags(),
+ kNewTypeIndex));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
+ for (const auto& entry : *methods) {
+ if (entry.IsResolved()) {
+ GetMethods(deps, GetNextResolutionKind(resolution_kind))->insert(
+ VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+ entry.GetAccessFlags(),
+ entry.GetDeclaringClassIndex()));
+ found = true;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
+ for (const auto& entry : *methods) {
+ if (entry.IsResolved()) {
+ GetMethods(deps, GetNextResolutionKind(GetNextResolutionKind(resolution_kind)))->insert(
+ VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+ entry.GetAccessFlags(),
+ entry.GetDeclaringClassIndex()));
+ found = true;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+ }
+}
+
} // namespace verifier
} // namespace art
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index 149861c..bdf63cb 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -607,5 +607,266 @@
}
}
+bool VerifierDeps::Verify(Handle<mirror::ClassLoader> class_loader, Thread* self) const {
+ for (const auto& entry : dex_deps_) {
+ if (!VerifyDexFile(class_loader, *entry.first, *entry.second, self)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// TODO: share that helper with other parts of the compiler that have
+// the same lookup pattern.
+static mirror::Class* FindClassAndClearException(ClassLinker* class_linker,
+ Thread* self,
+ const char* name,
+ Handle<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ mirror::Class* result = class_linker->FindClass(self, name, class_loader);
+ if (result == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
+ }
+ return result;
+}
+
+bool VerifierDeps::VerifyAssignability(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<TypeAssignability>& assignables,
+ bool expected_assignability,
+ Thread* self) const {
+ StackHandleScope<2> hs(self);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ MutableHandle<mirror::Class> source(hs.NewHandle<mirror::Class>(nullptr));
+ MutableHandle<mirror::Class> destination(hs.NewHandle<mirror::Class>(nullptr));
+
+ for (const auto& entry : assignables) {
+ const std::string& destination_desc = GetStringFromId(dex_file, entry.GetDestination());
+ destination.Assign(
+ FindClassAndClearException(class_linker, self, destination_desc.c_str(), class_loader));
+ const std::string& source_desc = GetStringFromId(dex_file, entry.GetSource());
+ source.Assign(
+ FindClassAndClearException(class_linker, self, source_desc.c_str(), class_loader));
+
+ if (destination.Get() == nullptr) {
+ LOG(INFO) << "VerifiersDeps: Could not resolve class " << destination_desc;
+ return false;
+ }
+
+ if (source.Get() == nullptr) {
+ LOG(INFO) << "VerifierDeps: Could not resolve class " << source_desc;
+ return false;
+ }
+
+ DCHECK(destination->IsResolved() && source->IsResolved());
+ if (destination->IsAssignableFrom(source.Get()) != expected_assignability) {
+ LOG(INFO) << "VerifierDeps: Class "
+ << destination_desc
+ << (expected_assignability ? " not " : " ")
+ << "assignable from "
+ << source_desc;
+ return false;
+ }
+ }
+ return true;
+}
+
+bool VerifierDeps::VerifyClasses(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<ClassResolution>& classes,
+ Thread* self) const {
+ StackHandleScope<1> hs(self);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr));
+ for (const auto& entry : classes) {
+ const char* descriptor = dex_file.StringByTypeIdx(entry.GetDexTypeIndex());
+ cls.Assign(FindClassAndClearException(class_linker, self, descriptor, class_loader));
+
+ if (entry.IsResolved()) {
+ if (cls.Get() == nullptr) {
+ LOG(INFO) << "VerifierDeps: Could not resolve class " << descriptor;
+ return false;
+ } else if (entry.GetAccessFlags() != GetAccessFlags(cls.Get())) {
+ LOG(INFO) << "VerifierDeps: Unexpected access flags on class "
+ << descriptor
+ << std::hex
+ << " (expected="
+ << entry.GetAccessFlags()
+ << ", actual="
+ << GetAccessFlags(cls.Get()) << ")"
+ << std::dec;
+ return false;
+ }
+ } else if (cls.Get() != nullptr) {
+ LOG(INFO) << "VerifierDeps: Unexpected successful resolution of class " << descriptor;
+ return false;
+ }
+ }
+ return true;
+}
+
+static std::string GetFieldDescription(const DexFile& dex_file, uint32_t index) {
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(index);
+ return std::string(dex_file.GetFieldDeclaringClassDescriptor(field_id))
+ + "->"
+ + dex_file.GetFieldName(field_id)
+ + ":"
+ + dex_file.GetFieldTypeDescriptor(field_id);
+}
+
+bool VerifierDeps::VerifyFields(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<FieldResolution>& fields,
+ Thread* self) const {
+ // Check recorded fields are resolved the same way, have the same recorded class,
+ // and have the same recorded flags.
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ StackHandleScope<1> hs(self);
+ Handle<mirror::DexCache> dex_cache(
+ hs.NewHandle(class_linker->FindDexCache(self, dex_file, /* allow_failure */ false)));
+ for (const auto& entry : fields) {
+ ArtField* field = class_linker->ResolveFieldJLS(
+ dex_file, entry.GetDexFieldIndex(), dex_cache, class_loader);
+
+ if (field == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
+ }
+
+ if (entry.IsResolved()) {
+ std::string expected_decl_klass = GetStringFromId(dex_file, entry.GetDeclaringClassIndex());
+ std::string temp;
+ if (field == nullptr) {
+ LOG(INFO) << "VerifierDeps: Could not resolve field "
+ << GetFieldDescription(dex_file, entry.GetDexFieldIndex());
+ return false;
+ } else if (expected_decl_klass != field->GetDeclaringClass()->GetDescriptor(&temp)) {
+ LOG(INFO) << "VerifierDeps: Unexpected declaring class for field resolution "
+ << GetFieldDescription(dex_file, entry.GetDexFieldIndex())
+ << " (expected=" << expected_decl_klass
+ << ", actual=" << field->GetDeclaringClass()->GetDescriptor(&temp) << ")";
+ return false;
+ } else if (entry.GetAccessFlags() != GetAccessFlags(field)) {
+ LOG(INFO) << "VerifierDeps: Unexpected access flags for resolved field "
+ << GetFieldDescription(dex_file, entry.GetDexFieldIndex())
+ << std::hex << " (expected=" << entry.GetAccessFlags()
+ << ", actual=" << GetAccessFlags(field) << ")" << std::dec;
+ return false;
+ }
+ } else if (field != nullptr) {
+ LOG(INFO) << "VerifierDeps: Unexpected successful resolution of field "
+ << GetFieldDescription(dex_file, entry.GetDexFieldIndex());
+ return false;
+ }
+ }
+ return true;
+}
+
+static std::string GetMethodDescription(const DexFile& dex_file, uint32_t index) {
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(index);
+ return std::string(dex_file.GetMethodDeclaringClassDescriptor(method_id))
+ + "->"
+ + dex_file.GetMethodName(method_id)
+ + dex_file.GetMethodSignature(method_id).ToString();
+}
+
+bool VerifierDeps::VerifyMethods(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<MethodResolution>& methods,
+ MethodResolutionKind kind,
+ Thread* self) const {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ PointerSize pointer_size = class_linker->GetImagePointerSize();
+
+ for (const auto& entry : methods) {
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(entry.GetDexMethodIndex());
+
+ const char* name = dex_file.GetMethodName(method_id);
+ const Signature signature = dex_file.GetMethodSignature(method_id);
+ const char* descriptor = dex_file.GetMethodDeclaringClassDescriptor(method_id);
+
+ mirror::Class* cls = FindClassAndClearException(class_linker, self, descriptor, class_loader);
+ if (cls == nullptr) {
+ LOG(INFO) << "VerifierDeps: Could not resolve class " << descriptor;
+ return false;
+ }
+ DCHECK(cls->IsResolved());
+ ArtMethod* method = nullptr;
+ if (kind == kDirectMethodResolution) {
+ method = cls->FindDirectMethod(name, signature, pointer_size);
+ } else if (kind == kVirtualMethodResolution) {
+ method = cls->FindVirtualMethod(name, signature, pointer_size);
+ } else {
+ DCHECK_EQ(kind, kInterfaceMethodResolution);
+ method = cls->FindInterfaceMethod(name, signature, pointer_size);
+ }
+
+ if (entry.IsResolved()) {
+ std::string temp;
+ std::string expected_decl_klass = GetStringFromId(dex_file, entry.GetDeclaringClassIndex());
+ if (method == nullptr) {
+ LOG(INFO) << "VerifierDeps: Could not resolve "
+ << kind
+ << " method "
+ << GetMethodDescription(dex_file, entry.GetDexMethodIndex());
+ return false;
+ } else if (expected_decl_klass != method->GetDeclaringClass()->GetDescriptor(&temp)) {
+ LOG(INFO) << "VerifierDeps: Unexpected declaring class for "
+ << kind
+ << " method resolution "
+ << GetMethodDescription(dex_file, entry.GetDexMethodIndex())
+ << " (expected="
+ << expected_decl_klass
+ << ", actual="
+ << method->GetDeclaringClass()->GetDescriptor(&temp)
+ << ")";
+ return false;
+ } else if (entry.GetAccessFlags() != GetAccessFlags(method)) {
+ LOG(INFO) << "VerifierDeps: Unexpected access flags for resolved "
+ << kind
+ << " method resolution "
+ << GetMethodDescription(dex_file, entry.GetDexMethodIndex())
+ << std::hex
+ << " (expected="
+ << entry.GetAccessFlags()
+ << ", actual="
+ << GetAccessFlags(method) << ")"
+ << std::dec;
+ return false;
+ }
+ } else if (method != nullptr) {
+ LOG(INFO) << "VerifierDeps: Unexpected successful resolution of "
+ << kind
+ << " method "
+ << GetMethodDescription(dex_file, entry.GetDexMethodIndex());
+ return false;
+ }
+ }
+ return true;
+}
+
+bool VerifierDeps::VerifyDexFile(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const DexFileDeps& deps,
+ Thread* self) const {
+ bool result = VerifyAssignability(
+ class_loader, dex_file, deps.assignable_types_, /* expected_assignability */ true, self);
+ result = result && VerifyAssignability(
+ class_loader, dex_file, deps.unassignable_types_, /* expected_assignability */ false, self);
+
+ result = result && VerifyClasses(class_loader, dex_file, deps.classes_, self);
+ result = result && VerifyFields(class_loader, dex_file, deps.fields_, self);
+
+ result = result && VerifyMethods(
+ class_loader, dex_file, deps.direct_methods_, kDirectMethodResolution, self);
+ result = result && VerifyMethods(
+ class_loader, dex_file, deps.virtual_methods_, kVirtualMethodResolution, self);
+ result = result && VerifyMethods(
+ class_loader, dex_file, deps.interface_methods_, kInterfaceMethodResolution, self);
+
+ return result;
+}
+
} // namespace verifier
} // namespace art
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index fab4323..eea0299 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -109,6 +109,11 @@
void Dump(VariableIndentationOutputStream* vios) const
NO_THREAD_SAFETY_ANALYSIS;
+ // Verify the encoded dependencies of this `VerifierDeps`.
+ // NO_THREAD_SAFETY_ANALYSIS, as this must be called on a read-only `VerifierDeps`.
+ bool Verify(Handle<mirror::ClassLoader> class_loader, Thread* self) const
+ NO_THREAD_SAFETY_ANALYSIS;
+
private:
static constexpr uint16_t kUnresolvedMarker = static_cast<uint16_t>(-1);
@@ -255,6 +260,54 @@
bool Equals(const VerifierDeps& rhs) const
REQUIRES(!Locks::verifier_deps_lock_);
+ // Verify `dex_file` according to the `deps`, that is going over each
+ // `DexFileDeps` field, and checking that the recorded information still
+ // holds.
+ bool VerifyDexFile(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const DexFileDeps& deps,
+ Thread* self) const
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::verifier_deps_lock_);
+
+ bool VerifyAssignability(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<TypeAssignability>& assignables,
+ bool expected_assignability,
+ Thread* self) const
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::verifier_deps_lock_);
+
+ // Verify that the set of resolved classes at the point of creation
+ // of this `VerifierDeps` is still the same.
+ bool VerifyClasses(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<ClassResolution>& classes,
+ Thread* self) const
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::verifier_deps_lock_);
+
+ // Verify that the set of resolved fields at the point of creation
+ // of this `VerifierDeps` is still the same, and each field resolves to the
+ // same field holder and access flags.
+ bool VerifyFields(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<FieldResolution>& classes,
+ Thread* self) const
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::verifier_deps_lock_);
+
+ // Verify that the set of resolved methods at the point of creation
+ // of this `VerifierDeps` is still the same, and each method resolves to the
+ // same method holder, access flags, and invocation kind.
+ bool VerifyMethods(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<MethodResolution>& methods,
+ MethodResolutionKind kind,
+ Thread* self) const
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::verifier_deps_lock_);
+
// Map from DexFiles into dependencies collected from verification of their methods.
std::map<const DexFile*, std::unique_ptr<DexFileDeps>> dex_deps_
GUARDED_BY(Locks::verifier_deps_lock_);
@@ -263,6 +316,7 @@
ART_FRIEND_TEST(VerifierDepsTest, StringToId);
ART_FRIEND_TEST(VerifierDepsTest, EncodeDecode);
ART_FRIEND_TEST(VerifierDepsTest, EncodeDecodeMulti);
+ ART_FRIEND_TEST(VerifierDepsTest, VerifyDeps);
};
} // namespace verifier