Suppress compilation of malformed dexfiles to prevent compiler segfault.
Generic failures detected at compile time will not be fixed at runtime,
so classes with generic failures are now added to a rejected classes
set and not compiled at all. This prevents the compiler from
segfaulting on certain occasions when registers and indexes are out of
range, etc. There is one remaining segfault in the vm-tests now.
Change-Id: Id67c12fd13f3e993a6a16a8625620daa0ea496cb
diff --git a/src/compiler.cc b/src/compiler.cc
index 9a49cdd..b7583d9 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -60,6 +60,14 @@
ByteArray* CreateJniDlsymLookupStub();
}
+namespace verifier {
+ class DexVerifier {
+ public:
+ static const std::vector<uint8_t>* GetGcMap(Compiler::MethodReference ref);
+ static bool IsClassRejected(Compiler::ClassReference ref);
+ };
+}
+
static double Percentage(size_t x, size_t y) {
return 100.0 * ((double)x) / ((double)(x + y));
}
@@ -920,6 +928,11 @@
if (SkipClass(class_loader, dex_file, class_def)) {
return;
}
+ ClassReference ref(&dex_file, class_def_index);
+ // Skip compiling classes with generic verifier failures since they will still fail at runtime
+ if (verifier::DexVerifier::IsClassRejected(ref)) {
+ return;
+ }
const byte* class_data = dex_file.GetClassData(class_def);
if (class_data == NULL) {
// empty class, probably a marker interface
@@ -1114,13 +1127,6 @@
}
}
-namespace verifier {
- class DexVerifier {
- public:
- static const std::vector<uint8_t>* GetGcMap(Compiler::MethodReference ref);
- };
-}
-
void Compiler::SetGcMapsMethod(const DexFile& dex_file, Method* method) {
if (method == NULL) {
Thread::Current()->ClearException();
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
index 04829bf..b68c7d5 100644
--- a/src/dex_verifier.cc
+++ b/src/dex_verifier.cc
@@ -1043,12 +1043,12 @@
std::ostream& DexVerifier::Fail(VerifyError error) {
CHECK_EQ(failure_, VERIFY_ERROR_NONE);
- // If we're optimistically running verification at compile time, turn NO_xxx and ACCESS_xxx
- // errors into generic errors so that we re-verify at runtime. We may fail to find or to agree
- // on access because of not yet available class loaders, or class loaders that will differ at
- // runtime.
if (Runtime::Current()->IsCompiler()) {
- switch(error) {
+ switch (error) {
+ // If we're optimistically running verification at compile time, turn NO_xxx and ACCESS_xxx
+ // errors into generic errors so that we re-verify at runtime. We may fail to find or to agree
+ // on access because of not yet available class loaders, or class loaders that will differ at
+ // runtime.
case VERIFY_ERROR_NO_CLASS:
case VERIFY_ERROR_NO_FIELD:
case VERIFY_ERROR_NO_METHOD:
@@ -1057,6 +1057,15 @@
case VERIFY_ERROR_ACCESS_METHOD:
error = VERIFY_ERROR_GENERIC;
break;
+ // Generic failures at compile time will still fail at runtime, so the class is marked as
+ // rejected to prevent it from being compiled.
+ case VERIFY_ERROR_GENERIC: {
+ const DexFile::ClassDef* class_def = ClassHelper(method_->GetDeclaringClass()).GetClassDef();
+ CHECK(class_def != NULL);
+ Compiler::ClassReference ref(dex_file_, dex_file_->GetIndexForClassDef(*class_def));
+ AddRejectedClass(ref);
+ break;
+ }
default:
break;
}
@@ -1168,7 +1177,7 @@
for(uint32_t dex_pc = 0; dex_pc < insns_size;) {
if (!VerifyInstruction(inst, dex_pc)) {
DCHECK_NE(failure_, VERIFY_ERROR_NONE);
- fail_messages_ << "Rejecting opcode " << inst->DumpString(dex_file_) << " at " << dex_pc;
+ fail_messages_ << "Rejecting opcode " << inst->Name() << " at " << dex_pc;
return false;
}
/* Flag instructions that are garbage collection points */
@@ -3073,6 +3082,10 @@
if (klass == NULL && !result.IsUnresolvedTypes()) {
method_->GetDexCacheResolvedTypes()->Set(class_idx, result.GetClass());
}
+ if (result.IsUnknown()) {
+ Fail(VERIFY_ERROR_GENERIC) << "accessing unknown class in " << PrettyDescriptor(referrer);
+ return result;
+ }
// Check if access is allowed. Unresolved types use AllocObjectFromCodeWithAccessCheck to
// check at runtime if access is allowed and so pass here.
if (!result.IsUnresolvedTypes() && !referrer->CanAccess(result.GetClass())) {
@@ -3962,6 +3975,20 @@
STLDeleteValues(&gc_maps_);
}
+Mutex DexVerifier::rejected_classes_lock_("verifier rejected classes lock");
+std::set<Compiler::ClassReference> DexVerifier::rejected_classes_;
+
+void DexVerifier::AddRejectedClass(Compiler::ClassReference ref) {
+ MutexLock mu(rejected_classes_lock_);
+ rejected_classes_.insert(ref);
+ CHECK(IsClassRejected(ref));
+}
+
+bool DexVerifier::IsClassRejected(Compiler::ClassReference ref) {
+ MutexLock mu(rejected_classes_lock_);
+ return (rejected_classes_.find(ref) != rejected_classes_.end());
+}
+
#if defined(ART_USE_LLVM_COMPILER)
InferredRegCategoryMap const* DexVerifier::GenerateInferredRegCategoryMap() {
uint32_t insns_size = code_item_->insns_size_in_code_units_;
diff --git a/src/dex_verifier.h b/src/dex_verifier.h
index b898859..3008793 100644
--- a/src/dex_verifier.h
+++ b/src/dex_verifier.h
@@ -20,6 +20,7 @@
#include <deque>
#include <limits>
#include <map>
+#include <set>
#include <vector>
#include "casts.h"
@@ -926,6 +927,8 @@
static const std::vector<uint8_t>* GetGcMap(Compiler::MethodReference ref);
static void DeleteGcMaps();
+ static bool IsClassRejected(Compiler::ClassReference ref);
+
private:
explicit DexVerifier(Method* method);
@@ -1260,6 +1263,11 @@
static GcMapTable gc_maps_;
static void SetGcMap(Compiler::MethodReference ref, const std::vector<uint8_t>& gc_map);
+ // Set of rejected classes that skip compilation
+ static Mutex rejected_classes_lock_;
+ static std::set<Compiler::ClassReference> rejected_classes_;
+ static void AddRejectedClass(Compiler::ClassReference ref);
+
RegTypeCache reg_types_;
PcToRegisterLineTable reg_table_;