Move super class verification to class linker
Verify super classes in the class linker and chain exceptions when super
class verification fails. Add support for dumping chained exceptions to
Throwable::Dump. Improve verifier error detail messages.
Fixes problem where super class verification would leave a pending
exception that would be tripped over elsewhere.
Change-Id: I3ca850fc66674c8601132d7ec40bebbf9c108ae3
diff --git a/src/class_linker.cc b/src/class_linker.cc
index cccf0f6..51e5f3c 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1889,9 +1889,41 @@
CHECK_EQ(klass->GetStatus(), Class::kStatusResolved) << PrettyClass(klass);
klass->SetStatus(Class::kStatusVerifying);
+ // Verify super class
+ Class* super = klass->GetSuperClass();
+ std::string error_msg;
+ if (super != NULL) {
+ // Acquire lock to prevent races on verifying the super class
+ ObjectLock lock(super);
+
+ if (!super->IsVerified() && !super->IsErroneous()) {
+ Runtime::Current()->GetClassLinker()->VerifyClass(super);
+ }
+ if (!super->IsVerified()) {
+ error_msg = "Rejecting class ";
+ error_msg += PrettyDescriptor(klass);
+ error_msg += " that attempts to sub-class erroneous class ";
+ error_msg += PrettyDescriptor(super);
+ LOG(ERROR) << error_msg << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8();
+ Thread* self = Thread::Current();
+ SirtRef<Throwable> cause(self->GetException());
+ if (cause.get() != NULL) {
+ self->ClearException();
+ }
+ self->ThrowNewException("Ljava/lang/VerifyError;", error_msg.c_str());
+ if (cause.get() != NULL) {
+ self->GetException()->SetCause(cause.get());
+ }
+ CHECK_EQ(klass->GetStatus(), Class::kStatusVerifying) << PrettyDescriptor(klass);
+ klass->SetStatus(Class::kStatusError);
+ return;
+ }
+ }
+
// Try to use verification information from oat file, otherwise do runtime verification
const DexFile& dex_file = FindDexFile(klass->GetDexCache());
- if (VerifyClassUsingOatFile(dex_file, klass) || verifier::DexVerifier::VerifyClass(klass)) {
+ if (VerifyClassUsingOatFile(dex_file, klass) ||
+ verifier::DexVerifier::VerifyClass(klass, error_msg)) {
// Make sure all classes referenced by catch blocks are resolved
ResolveClassExceptionHandlerTypes(dex_file, klass);
klass->SetStatus(Class::kStatusVerified);
@@ -1899,12 +1931,12 @@
CheckMethodsHaveGcMaps(klass);
} else {
LOG(ERROR) << "Verification failed on class " << PrettyDescriptor(klass)
- << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8();
+ << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8()
+ << " because: " << error_msg;
Thread* self = Thread::Current();
CHECK(!self->IsExceptionPending()) << self->GetException()->Dump();
- self->ThrowNewExceptionF("Ljava/lang/VerifyError;", "Verification of %s failed",
- PrettyDescriptor(klass).c_str());
- CHECK_EQ(klass->GetStatus(), Class::kStatusVerifying) << PrettyClass(klass);
+ self->ThrowNewException("Ljava/lang/VerifyError;", error_msg.c_str());
+ CHECK_EQ(klass->GetStatus(), Class::kStatusVerifying) << PrettyDescriptor(klass);
klass->SetStatus(Class::kStatusError);
}
}
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
index 31c2697..8776458 100644
--- a/src/dex_verifier.cc
+++ b/src/dex_verifier.cc
@@ -922,45 +922,41 @@
}
}
-bool DexVerifier::VerifyClass(const Class* klass) {
+bool DexVerifier::VerifyClass(const Class* klass, std::string& error) {
if (klass->IsVerified()) {
return true;
}
Class* super = klass->GetSuperClass();
if (super == NULL && StringPiece(ClassHelper(klass).GetDescriptor()) != "Ljava/lang/Object;") {
- LOG(ERROR) << "Verifier rejected class " << PrettyClass(klass) << " that has no super class";
+ error = "Verifier rejected class ";
+ error += PrettyDescriptor(klass);
+ error += " that has no super class";
return false;
}
- if (super != NULL) {
- // Acquire lock to prevent races on verifying the super class
- ObjectLock lock(super);
-
- if (!super->IsVerified() && !super->IsErroneous()) {
- Runtime::Current()->GetClassLinker()->VerifyClass(super);
- }
- if (!super->IsVerified()) {
- LOG(ERROR) << "Verifier rejected class " << PrettyClass(klass)
- << " that attempts to sub-class corrupt class " << PrettyClass(super);
- return false;
- } else if (super->IsFinal()) {
- LOG(ERROR) << "Verifier rejected class " << PrettyClass(klass)
- << " that attempts to sub-class final class " << PrettyClass(super);
- return false;
- }
+ if (super != NULL && super->IsFinal()) {
+ error = "Verifier rejected class ";
+ error += PrettyDescriptor(klass);
+ error += " that attempts to sub-class final class ";
+ error += PrettyDescriptor(super);
+ return false;
}
for (size_t i = 0; i < klass->NumDirectMethods(); ++i) {
Method* method = klass->GetDirectMethod(i);
if (!VerifyMethod(method)) {
- LOG(ERROR) << "Verifier rejected class " << PrettyClass(klass) << " due to bad method "
- << PrettyMethod(method, true);
+ error = "Verifier rejected class ";
+ error += PrettyDescriptor(klass);
+ error += " due to bad method ";
+ error += PrettyMethod(method, true);
return false;
}
}
for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) {
Method* method = klass->GetVirtualMethod(i);
if (!VerifyMethod(method)) {
- LOG(ERROR) << "Verifier rejected class " << PrettyClass(klass) << " due to bad method "
- << PrettyMethod(method, true);
+ error = "Verifier rejected class ";
+ error += PrettyDescriptor(klass);
+ error += " due to bad method ";
+ error += PrettyMethod(method, true);
return false;
}
}
diff --git a/src/dex_verifier.h b/src/dex_verifier.h
index cbdc7fd..0488ba3 100644
--- a/src/dex_verifier.h
+++ b/src/dex_verifier.h
@@ -826,7 +826,7 @@
class DexVerifier {
public:
/* Verify a class. Returns "true" on success. */
- static bool VerifyClass(const Class* klass);
+ static bool VerifyClass(const Class* klass, std::string& error);
/*
* Perform verification on a single method.
*
diff --git a/src/dex_verifier_test.cc b/src/dex_verifier_test.cc
index 379e8c4..51fb1bc 100644
--- a/src/dex_verifier_test.cc
+++ b/src/dex_verifier_test.cc
@@ -33,7 +33,8 @@
Class* klass = class_linker_->FindSystemClass(descriptor.c_str());
// Verify the class
- ASSERT_TRUE(DexVerifier::VerifyClass(klass));
+ std::string error_msg;
+ ASSERT_TRUE(DexVerifier::VerifyClass(klass, error_msg)) << error_msg;
}
void VerifyDexFile(const DexFile* dex, ClassLoader* class_loader) {
@@ -55,7 +56,8 @@
TEST_F(DexVerifierTest, IntMath) {
SirtRef<ClassLoader> class_loader(LoadDex("IntMath"));
Class* klass = class_linker_->FindClass("LIntMath;", class_loader.get());
- ASSERT_TRUE(DexVerifier::VerifyClass(klass));
+ std::string error_msg;
+ ASSERT_TRUE(DexVerifier::VerifyClass(klass, error_msg)) << error_msg;
}
TEST_F(DexVerifierTest, RegTypes_Primitives) {
diff --git a/src/object.cc b/src/object.cc
index 7f6e451..f90031a 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -1337,6 +1337,13 @@
return result;
}
+void Throwable::SetCause(Throwable* cause) {
+ CHECK(cause != NULL);
+ CHECK(cause != this);
+ CHECK(GetFieldObject<Throwable*>(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_), false) == NULL);
+ SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_), cause, false);
+}
+
bool Throwable::IsCheckedException() const {
Class* error = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/Error;");
if (InstanceOf(error)) {
@@ -1367,6 +1374,11 @@
result += "\n";
}
}
+ Throwable* cause = GetFieldObject<Throwable*>(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_), false);
+ if (cause != NULL) {
+ result += "Caused by: ";
+ result += cause->Dump();
+ }
return result;
}
diff --git a/src/object.h b/src/object.h
index 1841042..e9556ef 100644
--- a/src/object.h
+++ b/src/object.h
@@ -2273,6 +2273,10 @@
}
std::string Dump() const;
+ // This is a runtime version of initCause, you shouldn't use it if initCause may have been
+ // overridden. Also it asserts rather than throwing exceptions. Currently this is only used
+ // in cases like the verifier where the checks cannot fail and initCause isn't overridden.
+ void SetCause(Throwable* cause);
bool IsCheckedException() const;
private:
Object* GetStackState() const {