You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
356 lines
14 KiB
356 lines
14 KiB
/*
|
|
* 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,
|
|
/*verifier_deps=*/nullptr,
|
|
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,
|
|
VerifierDeps* verifier_deps,
|
|
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,
|
|
verifier_deps,
|
|
klass,
|
|
callbacks,
|
|
&svc,
|
|
allow_soft_failures,
|
|
log_level,
|
|
api_level,
|
|
error);
|
|
}
|
|
|
|
FailureKind ClassVerifier::CommonVerifyClass(Thread* self,
|
|
VerifierDeps* verifier_deps,
|
|
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,
|
|
verifier_deps,
|
|
&dex_file,
|
|
dex_cache,
|
|
class_loader,
|
|
*class_def,
|
|
callbacks,
|
|
verifier_callback,
|
|
allow_soft_failures,
|
|
log_level,
|
|
api_level,
|
|
error);
|
|
}
|
|
|
|
|
|
FailureKind ClassVerifier::VerifyClass(Thread* self,
|
|
VerifierDeps* verifier_deps,
|
|
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,
|
|
verifier_deps,
|
|
dex_file,
|
|
dex_cache,
|
|
class_loader,
|
|
class_def,
|
|
callbacks,
|
|
&svc,
|
|
allow_soft_failures,
|
|
log_level,
|
|
api_level,
|
|
error);
|
|
}
|
|
|
|
FailureKind ClassVerifier::VerifyClass(Thread* self,
|
|
VerifierDeps* verifier_deps,
|
|
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(),
|
|
verifier_deps,
|
|
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(UsToNs(elapsed_time_microseconds))
|
|
<< ", class: " << PrettyDescriptor(dex_file->GetClassDescriptor(class_def));
|
|
|
|
GetMetrics()->ClassVerificationCount()->AddOne();
|
|
|
|
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
|