| /* |
| * Copyright (C) 2011 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 "class_verifier.h" |
| |
| #include <android-base/logging.h> |
| #include <android-base/stringprintf.h> |
| |
| #include "art_method-inl.h" |
| #include "base/enums.h" |
| #include "base/locks.h" |
| #include "base/logging.h" |
| #include "base/systrace.h" |
| #include "base/utils.h" |
| #include "class_linker.h" |
| #include "compiler_callbacks.h" |
| #include "dex/class_accessor-inl.h" |
| #include "dex/class_reference.h" |
| #include "dex/descriptors_names.h" |
| #include "dex/dex_file-inl.h" |
| #include "handle.h" |
| #include "handle_scope-inl.h" |
| #include "method_verifier-inl.h" |
| #include "mirror/class-inl.h" |
| #include "mirror/dex_cache.h" |
| #include "runtime.h" |
| #include "thread.h" |
| #include "verifier/method_verifier.h" |
| #include "verifier/reg_type_cache.h" |
| |
| namespace art { |
| namespace verifier { |
| |
| using android::base::StringPrintf; |
| |
| // We print a warning blurb about "dx --no-optimize" when we find monitor-locking issues. Make |
| // sure we only print this once. |
| static bool gPrintedDxMonitorText = false; |
| |
| class StandardVerifyCallback : public VerifierCallback { |
| public: |
| void SetDontCompile(ArtMethod* m, bool value) override REQUIRES_SHARED(Locks::mutator_lock_) { |
| if (value) { |
| m->SetDontCompile(); |
| } |
| } |
| void SetMustCountLocks(ArtMethod* m, bool value) override REQUIRES_SHARED(Locks::mutator_lock_) { |
| if (value) { |
| m->SetMustCountLocks(); |
| } |
| } |
| }; |
| |
| FailureKind ClassVerifier::ReverifyClass(Thread* self, |
| ObjPtr<mirror::Class> klass, |
| HardFailLogMode log_level, |
| uint32_t api_level, |
| std::string* error) { |
| DCHECK(!Runtime::Current()->IsAotCompiler()); |
| StackHandleScope<1> hs(self); |
| Handle<mirror::Class> h_klass(hs.NewHandle(klass)); |
| // We don't want to mess with these while other mutators are possibly looking at them. Instead we |
| // will wait until we can update them while everything is suspended. |
| class DelayedVerifyCallback : public VerifierCallback { |
| public: |
| void SetDontCompile(ArtMethod* m, bool value) override REQUIRES_SHARED(Locks::mutator_lock_) { |
| dont_compiles_.push_back({ m, value }); |
| } |
| void SetMustCountLocks(ArtMethod* m, bool value) override |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| count_locks_.push_back({ m, value }); |
| } |
| void UpdateFlags(bool skip_access_checks) REQUIRES(Locks::mutator_lock_) { |
| for (auto it : count_locks_) { |
| VLOG(verifier_debug) << "Setting " << it.first->PrettyMethod() << " count locks to " |
| << it.second; |
| if (it.second) { |
| it.first->SetMustCountLocks(); |
| } else { |
| it.first->ClearMustCountLocks(); |
| } |
| if (skip_access_checks && it.first->IsInvokable() && !it.first->IsNative()) { |
| it.first->SetSkipAccessChecks(); |
| } |
| } |
| for (auto it : dont_compiles_) { |
| VLOG(verifier_debug) << "Setting " << it.first->PrettyMethod() << " dont-compile to " |
| << it.second; |
| if (it.second) { |
| it.first->SetDontCompile(); |
| } else { |
| it.first->ClearDontCompile(); |
| } |
| } |
| } |
| |
| private: |
| std::vector<std::pair<ArtMethod*, bool>> dont_compiles_; |
| std::vector<std::pair<ArtMethod*, bool>> count_locks_; |
| }; |
| DelayedVerifyCallback dvc; |
| FailureKind res = CommonVerifyClass(self, |
| h_klass.Get(), |
| /*callbacks=*/nullptr, |
| &dvc, |
| /*allow_soft_failures=*/false, |
| log_level, |
| api_level, |
| error); |
| DCHECK_NE(res, FailureKind::kHardFailure); |
| ScopedThreadSuspension sts(Thread::Current(), ThreadState::kSuspended); |
| ScopedSuspendAll ssa("Update method flags for reverify"); |
| dvc.UpdateFlags(res == FailureKind::kNoFailure); |
| return res; |
| } |
| |
| FailureKind ClassVerifier::VerifyClass(Thread* self, |
| ObjPtr<mirror::Class> klass, |
| CompilerCallbacks* callbacks, |
| bool allow_soft_failures, |
| HardFailLogMode log_level, |
| uint32_t api_level, |
| std::string* error) { |
| if (klass->IsVerified()) { |
| return FailureKind::kNoFailure; |
| } |
| StandardVerifyCallback svc; |
| return CommonVerifyClass(self, |
| klass, |
| callbacks, |
| &svc, |
| allow_soft_failures, |
| log_level, |
| api_level, |
| error); |
| } |
| |
| FailureKind ClassVerifier::CommonVerifyClass(Thread* self, |
| ObjPtr<mirror::Class> klass, |
| CompilerCallbacks* callbacks, |
| VerifierCallback* verifier_callback, |
| bool allow_soft_failures, |
| HardFailLogMode log_level, |
| uint32_t api_level, |
| std::string* error) { |
| bool early_failure = false; |
| std::string failure_message; |
| const DexFile& dex_file = klass->GetDexFile(); |
| const dex::ClassDef* class_def = klass->GetClassDef(); |
| ObjPtr<mirror::Class> super = klass->GetSuperClass(); |
| std::string temp; |
| if (super == nullptr && strcmp("Ljava/lang/Object;", klass->GetDescriptor(&temp)) != 0) { |
| early_failure = true; |
| failure_message = " that has no super class"; |
| } else if (super != nullptr && super->IsFinal()) { |
| early_failure = true; |
| failure_message = " that attempts to sub-class final class " + super->PrettyDescriptor(); |
| } else if (class_def == nullptr) { |
| early_failure = true; |
| failure_message = " that isn't present in dex file " + dex_file.GetLocation(); |
| } |
| if (early_failure) { |
| *error = "Verifier rejected class " + klass->PrettyDescriptor() + failure_message; |
| if (callbacks != nullptr) { |
| ClassReference ref(&dex_file, klass->GetDexClassDefIndex()); |
| callbacks->ClassRejected(ref); |
| } |
| return FailureKind::kHardFailure; |
| } |
| StackHandleScope<2> hs(self); |
| Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache())); |
| Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader())); |
| return VerifyClass(self, |
| &dex_file, |
| dex_cache, |
| class_loader, |
| *class_def, |
| callbacks, |
| verifier_callback, |
| allow_soft_failures, |
| log_level, |
| api_level, |
| error); |
| } |
| |
| |
| FailureKind ClassVerifier::VerifyClass(Thread* self, |
| const DexFile* dex_file, |
| Handle<mirror::DexCache> dex_cache, |
| Handle<mirror::ClassLoader> class_loader, |
| const dex::ClassDef& class_def, |
| CompilerCallbacks* callbacks, |
| bool allow_soft_failures, |
| HardFailLogMode log_level, |
| uint32_t api_level, |
| std::string* error) { |
| StandardVerifyCallback svc; |
| return VerifyClass(self, |
| dex_file, |
| dex_cache, |
| class_loader, |
| class_def, |
| callbacks, |
| &svc, |
| allow_soft_failures, |
| log_level, |
| api_level, |
| error); |
| } |
| |
| FailureKind ClassVerifier::VerifyClass(Thread* self, |
| const DexFile* dex_file, |
| Handle<mirror::DexCache> dex_cache, |
| Handle<mirror::ClassLoader> class_loader, |
| const dex::ClassDef& class_def, |
| CompilerCallbacks* callbacks, |
| VerifierCallback* verifier_callback, |
| bool allow_soft_failures, |
| HardFailLogMode log_level, |
| uint32_t api_level, |
| std::string* error) { |
| // A class must not be abstract and final. |
| if ((class_def.access_flags_ & (kAccAbstract | kAccFinal)) == (kAccAbstract | kAccFinal)) { |
| *error = "Verifier rejected class "; |
| *error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); |
| *error += ": class is abstract and final."; |
| return FailureKind::kHardFailure; |
| } |
| |
| ClassAccessor accessor(*dex_file, class_def); |
| SCOPED_TRACE << "VerifyClass " << PrettyDescriptor(accessor.GetDescriptor()); |
| metrics::AutoTimer timer{GetMetrics()->ClassVerificationTotalTime()}; |
| |
| int64_t previous_method_idx[2] = { -1, -1 }; |
| MethodVerifier::FailureData failure_data; |
| ClassLinker* const linker = Runtime::Current()->GetClassLinker(); |
| |
| for (const ClassAccessor::Method& method : accessor.GetMethods()) { |
| int64_t* previous_idx = &previous_method_idx[method.IsStaticOrDirect() ? 0u : 1u]; |
| self->AllowThreadSuspension(); |
| const uint32_t method_idx = method.GetIndex(); |
| if (method_idx == *previous_idx) { |
| // smali can create dex files with two encoded_methods sharing the same method_idx |
| // http://code.google.com/p/smali/issues/detail?id=119 |
| continue; |
| } |
| *previous_idx = method_idx; |
| const InvokeType type = method.GetInvokeType(class_def.access_flags_); |
| ArtMethod* resolved_method = linker->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( |
| method_idx, dex_cache, class_loader, /* referrer= */ nullptr, type); |
| if (resolved_method == nullptr) { |
| DCHECK(self->IsExceptionPending()); |
| // We couldn't resolve the method, but continue regardless. |
| self->ClearException(); |
| } else { |
| DCHECK(resolved_method->GetDeclaringClassUnchecked() != nullptr) << type; |
| } |
| std::string hard_failure_msg; |
| MethodVerifier::FailureData result = |
| MethodVerifier::VerifyMethod(self, |
| linker, |
| Runtime::Current()->GetArenaPool(), |
| method_idx, |
| dex_file, |
| dex_cache, |
| class_loader, |
| class_def, |
| method.GetCodeItem(), |
| resolved_method, |
| method.GetAccessFlags(), |
| callbacks, |
| verifier_callback, |
| allow_soft_failures, |
| log_level, |
| /*need_precise_constants=*/ false, |
| api_level, |
| Runtime::Current()->IsAotCompiler(), |
| &hard_failure_msg); |
| if (result.kind == FailureKind::kHardFailure) { |
| if (failure_data.kind == FailureKind::kHardFailure) { |
| // If we logged an error before, we need a newline. |
| *error += "\n"; |
| } else { |
| // If we didn't log a hard failure before, print the header of the message. |
| *error += "Verifier rejected class "; |
| *error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); |
| *error += ":"; |
| } |
| *error += " "; |
| *error += hard_failure_msg; |
| } |
| failure_data.Merge(result); |
| } |
| uint64_t elapsed_time_microseconds = timer.Stop(); |
| VLOG(verifier) << "VerifyClass took " << PrettyDuration(elapsed_time_microseconds * 1000) |
| << ", class: " << PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); |
| |
| if (failure_data.kind == FailureKind::kNoFailure) { |
| return FailureKind::kNoFailure; |
| } else { |
| if ((failure_data.types & VERIFY_ERROR_LOCKING) != 0) { |
| // Print a warning about expected slow-down. Use a string temporary to print one contiguous |
| // warning. |
| std::string tmp = |
| StringPrintf("Class %s failed lock verification and will run slower.", |
| PrettyDescriptor(accessor.GetDescriptor()).c_str()); |
| if (!gPrintedDxMonitorText) { |
| tmp = tmp + "\nCommon causes for lock verification issues are non-optimized dex code\n" |
| "and incorrect proguard optimizations."; |
| gPrintedDxMonitorText = true; |
| } |
| LOG(WARNING) << tmp; |
| } |
| return failure_data.kind; |
| } |
| } |
| |
| void ClassVerifier::Init(ClassLinker* class_linker) { |
| MethodVerifier::Init(class_linker); |
| } |
| |
| void ClassVerifier::Shutdown() { |
| MethodVerifier::Shutdown(); |
| } |
| |
| void ClassVerifier::VisitStaticRoots(RootVisitor* visitor) { |
| MethodVerifier::VisitStaticRoots(visitor); |
| } |
| |
| } // namespace verifier |
| } // namespace art |