diff options
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/optimizing/optimizing_compiler.cc | 9 | ||||
| -rw-r--r-- | compiler/verifier_deps_test.cc | 322 |
2 files changed, 329 insertions, 2 deletions
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 8c769270b1..19fd6f95c3 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -170,6 +170,15 @@ class PassObserver : public ValueObject { if (visualizer_enabled_) { MutexLock mu(Thread::Current(), visualizer_dump_mutex_); *visualizer_output_ << visualizer_oss_.str(); + // The destructor of `visualizer_output_` is normally + // responsible for flushing (and closing) the stream, but it + // won't be invoked during fast exits in non-debug mode -- see + // art::Dex2Oat::~Dex2Oat, which explicitly abandons some + // objects (such as the compiler driver) in non-debug mode, to + // avoid the cost of destructing them. Therefore we explicitly + // flush the stream here to prevent truncated CFG visualizer + // files. + visualizer_output_->flush(); } } diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index cd6ca4c6df..8d2a0e7860 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -399,6 +399,18 @@ class VerifierDepsTest : public CommonCompilerTest { 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_; @@ -1076,7 +1088,7 @@ TEST_F(VerifierDepsTest, EncodeDecode) { verifier_deps_->Encode(dex_files_, &buffer); ASSERT_FALSE(buffer.empty()); - VerifierDeps decoded_deps(dex_files_, ArrayRef<uint8_t>(buffer)); + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); ASSERT_TRUE(verifier_deps_->Equals(decoded_deps)); } @@ -1102,7 +1114,7 @@ TEST_F(VerifierDepsTest, EncodeDecodeMulti) { } // Dump the new verifier deps to ensure it can properly read the data. - VerifierDeps decoded_deps(dex_files, ArrayRef<uint8_t>(buffer)); + VerifierDeps decoded_deps(dex_files, ArrayRef<const uint8_t>(buffer)); std::ostringstream stream; VariableIndentationOutputStream os(&stream); decoded_deps.Dump(&os); @@ -1121,5 +1133,311 @@ TEST_F(VerifierDepsTest, UnverifiedClasses) { 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 |