| /* |
| * 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_compiler_binding.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; |
| |
| static void UpdateMethodFlags(uint32_t method_index, |
| Handle<mirror::Class> klass, |
| Handle<mirror::DexCache> dex_cache, |
| CompilerCallbacks* callbacks, |
| int error_types) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| if (callbacks != nullptr && !CanCompilerHandleVerificationFailure(error_types)) { |
| MethodReference ref(dex_cache->GetDexFile(), method_index); |
| callbacks->AddUncompilableMethod(ref); |
| } |
| if (klass == nullptr) { |
| DCHECK(Runtime::Current()->IsAotCompiler()); |
| // Flags will be set at runtime. |
| return; |
| } |
| |
| // Mark methods with DontCompile/MustCountLocks flags. |
| ClassLinker* const linker = Runtime::Current()->GetClassLinker(); |
| ArtMethod* method = |
| klass->FindClassMethod(dex_cache.Get(), method_index, linker->GetImagePointerSize()); |
| DCHECK(method != nullptr); |
| DCHECK(method->GetDeclaringClass() == klass.Get()); |
| if (!CanCompilerHandleVerificationFailure(error_types)) { |
| method->SetDontCompile(); |
| } |
| if ((error_types & VerifyError::VERIFY_ERROR_LOCKING) != 0) { |
| method->SetMustCountLocks(); |
| } |
| } |
| |
| FailureKind ClassVerifier::VerifyClass(Thread* self, |
| VerifierDeps* verifier_deps, |
| const DexFile* dex_file, |
| Handle<mirror::Class> klass, |
| Handle<mirror::DexCache> dex_cache, |
| Handle<mirror::ClassLoader> class_loader, |
| const dex::ClassDef& class_def, |
| CompilerCallbacks* callbacks, |
| 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; |
| } |
| |
| // Note that `klass` can be a redefined class, not in the loader's table yet. |
| // Therefore, we do not use it for class resolution, but only when needing to |
| // update its methods' flags. |
| 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; |
| std::string hard_failure_msg; |
| MethodVerifier::FailureData result = |
| MethodVerifier::VerifyMethod(self, |
| linker, |
| Runtime::Current()->GetArenaPool(), |
| verifier_deps, |
| method_idx, |
| dex_file, |
| dex_cache, |
| class_loader, |
| class_def, |
| method.GetCodeItem(), |
| method.GetAccessFlags(), |
| log_level, |
| 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; |
| } else if (result.kind != FailureKind::kNoFailure) { |
| UpdateMethodFlags(method.GetIndex(), klass, dex_cache, callbacks, result.types); |
| if ((result.types & VerifyError::VERIFY_ERROR_LOCKING) != 0) { |
| // Print a warning about expected slow-down. |
| // Use a string temporary to print one contiguous warning. |
| std::string tmp = |
| StringPrintf("Method %s failed lock verification and will run slower.", |
| dex_file->PrettyMethod(method.GetIndex()).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; |
| } |
| } |
| |
| // Merge the result for the method into the global state for the class. |
| failure_data.Merge(result); |
| } |
| uint64_t elapsed_time_microseconds = timer.Stop(); |
| VLOG(verifier) << "VerifyClass took " << PrettyDuration(UsToNs(elapsed_time_microseconds)) |
| << ", class: " << PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); |
| |
| GetMetrics()->ClassVerificationCount()->AddOne(); |
| |
| GetMetrics()->ClassVerificationTotalTimeDelta()->Add(elapsed_time_microseconds); |
| GetMetrics()->ClassVerificationCountDelta()->AddOne(); |
| |
| if (failure_data.kind == verifier::FailureKind::kHardFailure && callbacks != nullptr) { |
| ClassReference ref(dex_file, dex_file->GetIndexForClassDef(class_def)); |
| callbacks->ClassRejected(ref); |
| } |
| |
| 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 |