diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/class_linker.h | 13 | ||||
| -rw-r--r-- | src/object_utils.h | 85 | ||||
| -rw-r--r-- | src/runtime_support.cc | 3 | ||||
| -rw-r--r-- | src/verifier/method_verifier.cc | 975 | ||||
| -rw-r--r-- | src/verifier/method_verifier.h | 105 | ||||
| -rw-r--r-- | src/verifier/reg_type.cc | 161 | ||||
| -rw-r--r-- | src/verifier/reg_type.h | 44 | ||||
| -rw-r--r-- | src/verifier/reg_type_cache.cc | 37 | ||||
| -rw-r--r-- | src/verifier/reg_type_cache.h | 7 | ||||
| -rw-r--r-- | src/verifier/reg_type_test.cc | 16 | ||||
| -rw-r--r-- | src/verifier/register_line.cc | 24 | ||||
| -rw-r--r-- | src/verifier/register_line.h | 6 |
12 files changed, 678 insertions, 798 deletions
diff --git a/src/class_linker.h b/src/class_linker.h index 1925a9dcdd..a8fa01d10b 100644 --- a/src/class_linker.h +++ b/src/class_linker.h @@ -194,6 +194,19 @@ class ClassLinker { const ClassLoader* class_loader, bool is_static); + Field* ResolveFieldJLS(uint32_t field_idx, const Method* referrer) { + Field* resolved_field = + referrer->GetDeclaringClass()->GetDexCache()->GetResolvedField(field_idx); + if (UNLIKELY(resolved_field == NULL)) { + Class* declaring_class = referrer->GetDeclaringClass(); + DexCache* dex_cache = declaring_class->GetDexCache(); + const ClassLoader* class_loader = declaring_class->GetClassLoader(); + const DexFile& dex_file = FindDexFile(dex_cache); + resolved_field = ResolveFieldJLS(dex_file, field_idx, dex_cache, class_loader); + } + return resolved_field; + } + // Resolve a field with a given ID from the DexFile, storing the // result in DexCache. The ClassLinker and ClassLoader are used as // in ResolveType. No is_static argument is provided so that Java diff --git a/src/object_utils.h b/src/object_utils.h index ceef18621d..59ef515b26 100644 --- a/src/object_utils.h +++ b/src/object_utils.h @@ -183,13 +183,7 @@ class ClassHelper { } std::string GetLocation() { - DexCache* dex_cache = GetDexCache(); - if (dex_cache != NULL && !klass_->IsProxyClass()) { - return dex_cache->GetLocation()->ToModifiedUtf8(); - } else { - // Arrays and proxies are generated and have no corresponding dex file location. - return "generated class"; - } + return GetDexCache()->GetLocation()->ToModifiedUtf8(); } const DexFile& GetDexFile() { @@ -202,16 +196,6 @@ class ClassHelper { return *result; } - DexCache* GetDexCache() { - DexCache* result = dex_cache_; - if (result == NULL) { - DCHECK(klass_ != NULL); - result = klass_->GetDexCache(); - dex_cache_ = result; - } - return result; - } - private: const DexFile::TypeList* GetInterfaceTypeList() { const DexFile::TypeList* result = interface_type_list_; @@ -225,6 +209,16 @@ class ClassHelper { return result; } + DexCache* GetDexCache() { + DexCache* result = dex_cache_; + if (result == NULL) { + DCHECK(klass_ != NULL); + result = klass_->GetDexCache(); + dex_cache_ = result; + } + return result; + } + ClassLinker* GetClassLinker() { ClassLinker* result = class_linker_; if (result == NULL) { @@ -406,7 +400,6 @@ class MethodHelper { SetMethod(new_m); shorty_ = NULL; } - const char* GetName() { const DexFile& dex_file = GetDexFile(); uint32_t dex_method_idx = method_->GetDexMethodIndex(); @@ -427,14 +420,12 @@ class MethodHelper { } } } - String* GetNameAsString() { const DexFile& dex_file = GetDexFile(); uint32_t dex_method_idx = method_->GetDexMethodIndex(); const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx); return GetClassLinker()->ResolveString(dex_file, method_id.name_idx_, GetDexCache()); } - const char* GetShorty() { const char* result = shorty_; if (result == NULL) { @@ -445,14 +436,12 @@ class MethodHelper { } return result; } - uint32_t GetShortyLength() { if (shorty_ == NULL) { GetShorty(); } return shorty_len_; } - const std::string GetSignature() { const DexFile& dex_file = GetDexFile(); uint32_t dex_method_idx = method_->GetDexMethodIndex(); @@ -462,17 +451,14 @@ class MethodHelper { return "<no signature>"; } } - const DexFile::ProtoId& GetPrototype() { const DexFile& dex_file = GetDexFile(); return dex_file.GetMethodPrototype(dex_file.GetMethodId(method_->GetDexMethodIndex())); } - const DexFile::TypeList* GetParameterTypeList() { const DexFile::ProtoId& proto = GetPrototype(); return GetDexFile().GetProtoParameters(proto); } - ObjectArray<Class>* GetParameterTypes() { const DexFile::TypeList* params = GetParameterTypeList(); Class* array_class = GetClassLinker()->FindSystemClass("[Ljava/lang/Class;"); @@ -488,7 +474,6 @@ class MethodHelper { } return result; } - Class* GetReturnType() { const DexFile& dex_file = GetDexFile(); const DexFile::MethodId& method_id = dex_file.GetMethodId(method_->GetDexMethodIndex()); @@ -496,7 +481,6 @@ class MethodHelper { uint16_t return_type_idx = proto_id.return_type_idx_; return GetClassFromTypeIdx(return_type_idx); } - const char* GetReturnTypeDescriptor() { const DexFile& dex_file = GetDexFile(); const DexFile::MethodId& method_id = dex_file.GetMethodId(method_->GetDexMethodIndex()); @@ -504,12 +488,10 @@ class MethodHelper { uint16_t return_type_idx = proto_id.return_type_idx_; return dex_file.GetTypeDescriptor(dex_file.GetTypeId(return_type_idx)); } - int32_t GetLineNumFromNativePC(uintptr_t raw_pc) { const DexFile& dex_file = GetDexFile(); return dex_file.GetLineNumFromPC(method_, method_->ToDexPC(raw_pc)); } - const char* GetDeclaringClassDescriptor() { Class* klass = method_->GetDeclaringClass(); DCHECK(!klass->IsProxyClass()); @@ -517,7 +499,6 @@ class MethodHelper { const DexFile& dex_file = GetDexFile(); return dex_file.GetTypeDescriptor(dex_file.GetTypeId(type_idx)); } - const char* GetDeclaringClassSourceFile() { const char* descriptor = GetDeclaringClassDescriptor(); const DexFile& dex_file = GetDexFile(); @@ -525,33 +506,17 @@ class MethodHelper { CHECK(dex_class_def != NULL); return dex_file.GetSourceFile(*dex_class_def); } - - uint32_t GetClassDefIndex() { - const char* descriptor = GetDeclaringClassDescriptor(); - const DexFile& dex_file = GetDexFile(); - uint32_t index; - CHECK(dex_file.FindClassDefIndex(descriptor, index)); - return index; - } - - ClassLoader* GetClassLoader() { - return method_->GetDeclaringClass()->GetClassLoader(); - } - bool IsStatic() { return method_->IsStatic(); } - bool IsClassInitializer() { return IsStatic() && StringPiece(GetName()) == "<clinit>"; } - size_t NumArgs() { // "1 +" because the first in Args is the receiver. // "- 1" because we don't count the return type. return (IsStatic() ? 0 : 1) + GetShortyLength() - 1; } - // Is the specified parameter a long or double, where parameter 0 is 'this' for instance methods bool IsParamALongOrDouble(size_t param) { CHECK_LT(param, NumArgs()); @@ -563,7 +528,6 @@ class MethodHelper { char ch = GetShorty()[param]; return (ch == 'J' || ch == 'D'); } - // Is the specified parameter a reference, where parameter 0 is 'this' for instance methods bool IsParamAReference(size_t param) { CHECK_LT(param, NumArgs()); @@ -574,7 +538,6 @@ class MethodHelper { } return GetShorty()[param] == 'L'; // An array also has a shorty character of 'L' (not '[') } - bool HasSameNameAndSignature(MethodHelper* other) { if (GetDexCache() == other->GetDexCache()) { const DexFile& dex_file = GetDexFile(); @@ -587,15 +550,12 @@ class MethodHelper { StringPiece other_name(other->GetName()); return name == other_name && GetSignature() == other->GetSignature(); } - const DexFile::CodeItem* GetCodeItem() { return GetDexFile().GetCodeItem(method_->GetCodeItemOffset()); } - bool IsResolvedTypeIdx(uint16_t type_idx) const { return method_->GetDexCacheResolvedTypes()->Get(type_idx) != NULL; } - Class* GetClassFromTypeIdx(uint16_t type_idx) { Class* type = method_->GetDexCacheResolvedTypes()->Get(type_idx); if (type == NULL) { @@ -604,16 +564,13 @@ class MethodHelper { } return type; } - const char* GetTypeDescriptorFromTypeIdx(uint16_t type_idx) { const DexFile& dex_file = GetDexFile(); return dex_file.GetTypeDescriptor(dex_file.GetTypeId(type_idx)); } - Class* GetDexCacheResolvedType(uint16_t type_idx) { return GetDexCache()->GetResolvedType(type_idx); } - const DexFile& GetDexFile() { const DexFile* result = dex_file_; if (result == NULL) { @@ -623,16 +580,6 @@ class MethodHelper { } return *result; } - - DexCache* GetDexCache() { - DexCache* result = dex_cache_; - if (result == NULL) { - Class* klass = method_->GetDeclaringClass(); - result = klass->GetDexCache(); - dex_cache_ = result; - } - return result; - } private: // Set the method_ field, for proxy methods looking up the interface method via the resolved // methods table. @@ -649,7 +596,15 @@ class MethodHelper { } method_ = method; } - + DexCache* GetDexCache() { + DexCache* result = dex_cache_; + if (result == NULL) { + Class* klass = method_->GetDeclaringClass(); + result = klass->GetDexCache(); + dex_cache_ = result; + } + return result; + } ClassLinker* GetClassLinker() { ClassLinker* result = class_linker_; if (result == NULL) { diff --git a/src/runtime_support.cc b/src/runtime_support.cc index 1152c79ff1..50f0130b4c 100644 --- a/src/runtime_support.cc +++ b/src/runtime_support.cc @@ -359,6 +359,9 @@ void ThrowVerificationError(Thread* self, const Method* method, case verifier::VERIFY_ERROR_BAD_CLASS_HARD: // Generic VerifyError; use default exception, no message. break; + case verifier::VERIFY_ERROR_NONE: + CHECK(false); + break; } self->ThrowNewException(exception_class, msg.c_str()); diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc index ad4d6ed183..197994053e 100644 --- a/src/verifier/method_verifier.cc +++ b/src/verifier/method_verifier.cc @@ -184,145 +184,174 @@ bool MethodVerifier::VerifyClass(const Class* klass, std::string& error) { error += PrettyDescriptor(super); return false; } - ClassHelper kh(klass); - const DexFile& dex_file = kh.GetDexFile(); - uint32_t class_def_idx; - if (!dex_file.FindClassDefIndex(kh.GetDescriptor(), class_def_idx)) { - error = "Verifier rejected class "; - error += PrettyDescriptor(klass); - error += " that isn't present in dex file "; - error += dex_file.GetLocation(); - return false; + for (size_t i = 0; i < klass->NumDirectMethods(); ++i) { + Method* method = klass->GetDirectMethod(i); + if (!VerifyMethod(method)) { + 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)) { + error = "Verifier rejected class "; + error += PrettyDescriptor(klass); + error += " due to bad method "; + error += PrettyMethod(method, true); + return false; + } + } + return true; +} + +bool MethodVerifier::VerifyMethod(Method* method) { + MethodVerifier verifier(method); + bool success = verifier.VerifyAll(); + CHECK_EQ(success, verifier.failure_ == VERIFY_ERROR_NONE); + + // We expect either success and no verification error, or failure and a generic failure to + // reject the class. + if (success) { + if (verifier.failure_ != VERIFY_ERROR_NONE) { + LOG(FATAL) << "Unhandled failure in verification of " << PrettyMethod(method) << std::endl + << verifier.fail_messages_; + } + } else { + LOG(INFO) << "Verification error in " << PrettyMethod(method) << " " + << verifier.fail_messages_.str(); + if (gDebugVerify) { + std::cout << std::endl << verifier.info_messages_.str(); + verifier.Dump(std::cout); + } + DCHECK((verifier.failure_ == VERIFY_ERROR_BAD_CLASS_SOFT) || + (verifier.failure_ == VERIFY_ERROR_BAD_CLASS_HARD)) << verifier.failure_; } - return VerifyClass(&dex_file, kh.GetDexCache(), klass->GetClassLoader(), class_def_idx, error); + return success; +} + +void MethodVerifier::VerifyMethodAndDump(Method* method) { + MethodVerifier verifier(method); + verifier.VerifyAll(); + + LOG(INFO) << "Dump of method " << PrettyMethod(method) << " " + << verifier.fail_messages_.str() << std::endl + << verifier.info_messages_.str() << Dumpable<MethodVerifier>(verifier); } bool MethodVerifier::VerifyClass(const DexFile* dex_file, DexCache* dex_cache, const ClassLoader* class_loader, uint32_t class_def_idx, std::string& error) { const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx); const byte* class_data = dex_file->GetClassData(class_def); - if (class_data == NULL) { - // empty class, probably a marker interface - return true; - } ClassDataItemIterator it(*dex_file, class_data); while (it.HasNextStaticField() || it.HasNextInstanceField()) { it.Next(); } - size_t error_count = 0; - ClassLinker* linker = Runtime::Current()->GetClassLinker(); while (it.HasNextDirectMethod()) { uint32_t method_idx = it.GetMemberIndex(); - Method* method = linker->ResolveMethod(*dex_file, method_idx, dex_cache, class_loader, true); - if (method == NULL) { - DCHECK(Thread::Current()->IsExceptionPending()); - // We couldn't resolve the method, but continue regardless. - Thread::Current()->ClearException(); - } if (!VerifyMethod(method_idx, dex_file, dex_cache, class_loader, class_def_idx, - it.GetMethodCodeItem(), method, it.GetMemberAccessFlags())) { - if (error_count > 0) { - error += "\n"; - } - error = "Verifier rejected class "; + it.GetMethodCodeItem())) { + error = "Verifier rejected class"; error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); error += " due to bad method "; error += PrettyMethod(method_idx, *dex_file); - ++error_count; + return false; } it.Next(); } while (it.HasNextVirtualMethod()) { uint32_t method_idx = it.GetMemberIndex(); - Method* method = linker->ResolveMethod(*dex_file, method_idx, dex_cache, class_loader, false); - if (method == NULL) { - DCHECK(Thread::Current()->IsExceptionPending()); - // We couldn't resolve the method, but continue regardless. - Thread::Current()->ClearException(); - } if (!VerifyMethod(method_idx, dex_file, dex_cache, class_loader, class_def_idx, - it.GetMethodCodeItem(), method, it.GetMemberAccessFlags())) { - if (error_count > 0) { - error += "\n"; - } - error = "Verifier rejected class "; + it.GetMethodCodeItem())) { + error = "Verifier rejected class"; error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); error += " due to bad method "; error += PrettyMethod(method_idx, *dex_file); - ++error_count; + return false; } it.Next(); } - return error_count == 0; + return true; } bool MethodVerifier::VerifyMethod(uint32_t method_idx, const DexFile* dex_file, DexCache* dex_cache, - const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item, - Method* method, uint32_t method_access_flags) { - MethodVerifier verifier(dex_file, dex_cache, class_loader, class_def_idx, code_item, method_idx, - method, method_access_flags); - bool success = verifier.Verify(); + const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item) { + MethodVerifier verifier(dex_file, dex_cache, class_loader, class_def_idx, code_item); + + // Without a method*, we can only verify the structure. + bool success = verifier.VerifyStructure(); + CHECK_EQ(success, verifier.failure_ == VERIFY_ERROR_NONE); + + // We expect either success and no verification error, or failure and a generic failure to + // reject the class. if (success) { - // Verification completed, however failures may be pending that didn't cause the verification - // to hard fail. - if (verifier.failures_.size() != 0) { - verifier.DumpFailures(LOG(INFO) << "Soft verification failures in " - << PrettyMethod(method_idx, *dex_file) << std::endl); - success = false; + if (verifier.failure_ != VERIFY_ERROR_NONE) { + LOG(FATAL) << "Unhandled failure in verification of " << PrettyMethod(method_idx, *dex_file) + << std::endl << verifier.fail_messages_; } } else { - // Bad method data. - CHECK_NE(verifier.failures_.size(), 0U); - CHECK(verifier.have_pending_hard_failure_); - verifier.DumpFailures(LOG(INFO) << "Verification error in " - << PrettyMethod(method_idx, *dex_file) << std::endl); + LOG(INFO) << "Verification error in " << PrettyMethod(method_idx, *dex_file) << " " + << verifier.fail_messages_.str(); if (gDebugVerify) { std::cout << std::endl << verifier.info_messages_.str(); verifier.Dump(std::cout); } + DCHECK((verifier.failure_ == VERIFY_ERROR_BAD_CLASS_SOFT) || + (verifier.failure_ == VERIFY_ERROR_BAD_CLASS_HARD)) << verifier.failure_; } return success; } -void MethodVerifier::VerifyMethodAndDump(Method* method) { +MethodVerifier::MethodVerifier(Method* method) + : work_insn_idx_(-1), + method_(method), + failure_(VERIFY_ERROR_NONE), + new_instance_count_(0), + monitor_enter_count_(0) { CHECK(method != NULL); - MethodHelper mh(method); - MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(), - mh.GetClassDefIndex(), mh.GetCodeItem(), method->GetDexMethodIndex(), - method, method->GetAccessFlags()); - verifier.Verify(); - verifier.DumpFailures(LOG(INFO) << "Dump of method " << PrettyMethod(method) << std::endl) - << verifier.info_messages_.str() << Dumpable<MethodVerifier>(verifier); + dex_cache_ = method->GetDeclaringClass()->GetDexCache(); + class_loader_ = method->GetDeclaringClass()->GetClassLoader(); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + dex_file_ = &class_linker->FindDexFile(dex_cache_); + code_item_ = dex_file_->GetCodeItem(method->GetCodeItemOffset()); + const DexFile::ClassDef* class_def = ClassHelper(method_->GetDeclaringClass()).GetClassDef(); + class_def_idx_ = dex_file_->GetIndexForClassDef(*class_def); } MethodVerifier::MethodVerifier(const DexFile* dex_file, DexCache* dex_cache, - const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item, - uint32_t method_idx, Method* method, uint32_t method_access_flags) + const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item) : work_insn_idx_(-1), - method_idx_(method_idx), - foo_method_(method), - method_access_flags_(method_access_flags), + method_(NULL), dex_file_(dex_file), dex_cache_(dex_cache), class_loader_(class_loader), class_def_idx_(class_def_idx), code_item_(code_item), - have_pending_hard_failure_(false), - have_pending_rewrite_failure_(false), + failure_(VERIFY_ERROR_NONE), new_instance_count_(0), monitor_enter_count_(0) { } -bool MethodVerifier::Verify() { +bool MethodVerifier::VerifyAll() { + CHECK(method_ != NULL); // If there aren't any instructions, make sure that's expected, then exit successfully. if (code_item_ == NULL) { - if ((method_access_flags_ & (kAccNative | kAccAbstract)) == 0) { + if (!method_->IsNative() && !method_->IsAbstract()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "zero-length code in concrete non-native method"; return false; } else { return true; } } + return VerifyStructure() && VerifyCodeFlow(); +} + +bool MethodVerifier::VerifyStructure() { + if (code_item_ == NULL) { + return true; + } // Sanity-check the register counts. ins + locals = registers, so make sure that ins <= registers. if (code_item_->ins_size_ > code_item_->registers_size_) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad register counts (ins=" << code_item_->ins_size_ @@ -337,73 +366,39 @@ bool MethodVerifier::Verify() { result = result && ScanTryCatchBlocks(); // Perform static instruction verification. result = result && VerifyInstructions(); - // Perform code-flow analysis and return. - return result && VerifyCodeFlow(); + return result; } std::ostream& MethodVerifier::Fail(VerifyError error) { - switch (error) { - case VERIFY_ERROR_NO_CLASS: - case VERIFY_ERROR_NO_FIELD: - case VERIFY_ERROR_NO_METHOD: - case VERIFY_ERROR_ACCESS_CLASS: - case VERIFY_ERROR_ACCESS_FIELD: - case VERIFY_ERROR_ACCESS_METHOD: - if (Runtime::Current()->IsCompiler()) { - // If we're optimistically running verification at compile time, turn NO_xxx and ACCESS_xxx - // errors into soft verification 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. + CHECK_EQ(failure_, VERIFY_ERROR_NONE); + if (Runtime::Current()->IsCompiler()) { + switch (error) { + // If we're optimistically running verification at compile time, turn NO_xxx and ACCESS_xxx + // errors into soft verification 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: + case VERIFY_ERROR_ACCESS_CLASS: + case VERIFY_ERROR_ACCESS_FIELD: + case VERIFY_ERROR_ACCESS_METHOD: error = VERIFY_ERROR_BAD_CLASS_SOFT; - } else { - have_pending_rewrite_failure_ = true; - } - break; - // Errors that are bad at both compile and runtime, but don't cause rejection of the class. - case VERIFY_ERROR_CLASS_CHANGE: - case VERIFY_ERROR_INSTANTIATION: - have_pending_rewrite_failure_ = true; - break; - // Indication that verification should be retried at runtime. - case VERIFY_ERROR_BAD_CLASS_SOFT: - if (!Runtime::Current()->IsCompiler()) { - // It is runtime so hard fail. - have_pending_hard_failure_ = true; - } - break; + break; // Hard verification 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_BAD_CLASS_HARD: { - if (Runtime::Current()->IsCompiler()) { + case VERIFY_ERROR_BAD_CLASS_HARD: { Compiler::ClassReference ref(dex_file_, class_def_idx_); AddRejectedClass(ref); + break; } - have_pending_hard_failure_ = true; - break; + default: + break; } } - failures_.push_back(error); - std::string location(StringPrintf("%s: [0x%X]", PrettyMethod(method_idx_, *dex_file_).c_str(), - work_insn_idx_)); - std::ostringstream* failure_message = new std::ostringstream(location); - failure_messages_.push_back(failure_message); - return *failure_message; -} - -void MethodVerifier::PrependToLastFailMessage(std::string prepend) { - size_t failure_num = failure_messages_.size(); - DCHECK_NE(failure_num, 0U); - std::ostringstream* last_fail_message = failure_messages_[failure_num - 1]; - prepend += last_fail_message->str(); - failure_messages_[failure_num - 1] = new std::ostringstream(prepend); - delete last_fail_message; -} - -void MethodVerifier::AppendToLastFailMessage(std::string append) { - size_t failure_num = failure_messages_.size(); - DCHECK_NE(failure_num, 0U); - std::ostringstream* last_fail_message = failure_messages_[failure_num - 1]; - (*last_fail_message) << append; + failure_ = error; + return fail_messages_ << "VFY: " << PrettyMethod(method_) + << '[' << reinterpret_cast<void*>(work_insn_idx_) << "] : "; } bool MethodVerifier::ComputeWidthsAndCountOps() { @@ -508,7 +503,8 @@ bool MethodVerifier::VerifyInstructions() { uint32_t insns_size = code_item_->insns_size_in_code_units_; for (uint32_t dex_pc = 0; dex_pc < insns_size;) { if (!VerifyInstruction(inst, dex_pc)) { - DCHECK_NE(failures_.size(), 0U); + DCHECK_NE(failure_, VERIFY_ERROR_NONE); + fail_messages_ << "Rejecting opcode " << inst->Name() << " at " << dex_pc; return false; } /* Flag instructions that are garbage collection points */ @@ -932,34 +928,30 @@ bool MethodVerifier::VerifyCodeFlow() { /* Initialize register types of method arguments. */ if (!SetTypesFromSignature()) { - DCHECK_NE(failures_.size(), 0U); - std::string prepend("Bad signature in "); - prepend += PrettyMethod(method_idx_, *dex_file_); - PrependToLastFailMessage(prepend); + DCHECK_NE(failure_, VERIFY_ERROR_NONE); + fail_messages_ << "Bad signature in " << PrettyMethod(method_); return false; } /* Perform code flow verification. */ if (!CodeFlowVerifyMethod()) { - DCHECK_NE(failures_.size(), 0U); + DCHECK_NE(failure_, VERIFY_ERROR_NONE); return false; } /* Generate a register map and add it to the method. */ UniquePtr<const std::vector<uint8_t> > map(GenerateGcMap()); if (map.get() == NULL) { - DCHECK_NE(failures_.size(), 0U); + DCHECK_NE(failure_, VERIFY_ERROR_NONE); return false; // Not a real failure, but a failure to encode } #ifndef NDEBUG VerifyGcMap(*map); #endif const std::vector<uint8_t>* gc_map = CreateLengthPrefixedGcMap(*(map.get())); - Compiler::MethodReference ref(dex_file_, method_idx_); + Compiler::MethodReference ref(dex_file_, method_->GetDexMethodIndex()); verifier::MethodVerifier::SetGcMap(ref, *gc_map); - if (foo_method_ != NULL) { - foo_method_->SetGcMap(&gc_map->at(0)); - } + method_->SetGcMap(&gc_map->at(0)); #if defined(ART_USE_LLVM_COMPILER) /* Generate Inferred Register Category for LLVM-based Code Generator */ @@ -970,18 +962,6 @@ bool MethodVerifier::VerifyCodeFlow() { return true; } -std::ostream& MethodVerifier::DumpFailures(std::ostream& os) { - DCHECK_EQ(failures_.size(), failure_messages_.size()); - for (size_t i = 0; i < failures_.size(); ++i) { - os << failure_messages_[i]->str() << std::endl; - } - return os; -} - -extern "C" void MethodVerifierGdbDump(MethodVerifier* v) { - v->Dump(std::cerr); -} - void MethodVerifier::Dump(std::ostream& os) { if (code_item_ == NULL) { os << "Native method" << std::endl; @@ -1025,22 +1005,22 @@ bool MethodVerifier::SetTypesFromSignature() { DCHECK_GE(arg_start, 0); /* should have been verified earlier */ //Include the "this" pointer. size_t cur_arg = 0; - if (!IsStatic()) { + if (!method_->IsStatic()) { // If this is a constructor for a class other than java.lang.Object, mark the first ("this") // argument as uninitialized. This restricts field access until the superclass constructor is // called. - const RegType& declaring_class = GetDeclaringClass(); - if (IsConstructor() && !declaring_class.IsJavaLangObject()) { + Class* declaring_class = method_->GetDeclaringClass(); + if (method_->IsConstructor() && !declaring_class->IsObjectClass()) { reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.UninitializedThisArgument(declaring_class)); } else { - reg_line->SetRegisterType(arg_start + cur_arg, declaring_class); + reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.FromClass(declaring_class)); } cur_arg++; } const DexFile::ProtoId& proto_id = - dex_file_->GetMethodPrototype(dex_file_->GetMethodId(method_idx_)); + dex_file_->GetMethodPrototype(dex_file_->GetMethodId(method_->GetDexMethodIndex())); DexFileParameterIterator iterator(*dex_file_, proto_id); for (; iterator.HasNext(); iterator.Next()) { @@ -1061,7 +1041,8 @@ bool MethodVerifier::SetTypesFromSignature() { // it's effectively considered initialized the instant we reach here (in the sense that we // can return without doing anything or call virtual methods). { - const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor); + const RegType& reg_type = + reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor); reg_line->SetRegisterType(arg_start + cur_arg, reg_type); } break; @@ -1182,7 +1163,7 @@ bool MethodVerifier::CodeFlowVerifyMethod() { if (work_line_->CompareLine(register_line) != 0) { Dump(std::cout); std::cout << info_messages_.str(); - LOG(FATAL) << "work_line diverged in " << PrettyMethod(method_idx_, *dex_file_) + LOG(FATAL) << "work_line diverged in " << PrettyMethod(method_) << "@" << reinterpret_cast<void*>(work_insn_idx_) << std::endl << " work_line=" << *work_line_ << std::endl << " expected=" << *register_line; @@ -1191,9 +1172,7 @@ bool MethodVerifier::CodeFlowVerifyMethod() { #endif } if (!CodeFlowVerifyInstruction(&start_guess)) { - std::string prepend(PrettyMethod(method_idx_, *dex_file_)); - prepend += " failed to verify: "; - PrependToLastFailMessage(prepend); + fail_messages_ << std::endl << PrettyMethod(method_) << " failed to verify"; return false; } /* Clear "changed" and mark as visited. */ @@ -1201,7 +1180,7 @@ bool MethodVerifier::CodeFlowVerifyMethod() { insn_flags_[insn_idx].ClearChanged(); } - if (DEAD_CODE_SCAN && ((method_access_flags_ & kAccWritable) == 0)) { + if (DEAD_CODE_SCAN && ((method_->GetAccessFlags() & kAccWritable) == 0)) { /* * Scan for dead code. There's nothing "evil" about dead code * (besides the wasted space), but it indicates a flaw somewhere @@ -1356,14 +1335,14 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { break; } case Instruction::RETURN_VOID: - if (!IsConstructor() || work_line_->CheckConstructorReturn()) { - if (!GetMethodReturnType().IsConflict()) { + if (!method_->IsConstructor() || work_line_->CheckConstructorReturn()) { + if (!GetMethodReturnType().IsUnknown()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-void not expected"; } } break; case Instruction::RETURN: - if (!IsConstructor() || work_line_->CheckConstructorReturn()) { + if (!method_->IsConstructor() || work_line_->CheckConstructorReturn()) { /* check the method signature */ const RegType& return_type = GetMethodReturnType(); if (!return_type.IsCategory1Types()) { @@ -1377,31 +1356,30 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { return_type.IsShort() || return_type.IsChar()) && src_type.IsInteger())); /* check the register contents */ - bool success = - work_line_->VerifyRegisterType(dec_insn.vA, use_src ? src_type : return_type); - if (!success) { - AppendToLastFailMessage(StringPrintf(" return-1nr on invalid register v%d", dec_insn.vA)); + work_line_->VerifyRegisterType(dec_insn.vA, use_src ? src_type : return_type); + if (failure_ != VERIFY_ERROR_NONE) { + fail_messages_ << " return-1nr on invalid register v" << dec_insn.vA; } } } break; case Instruction::RETURN_WIDE: - if (!IsConstructor() || work_line_->CheckConstructorReturn()) { + if (!method_->IsConstructor() || work_line_->CheckConstructorReturn()) { /* check the method signature */ const RegType& return_type = GetMethodReturnType(); if (!return_type.IsCategory2Types()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-wide not expected"; } else { /* check the register contents */ - bool success = work_line_->VerifyRegisterType(dec_insn.vA, return_type); - if (!success) { - AppendToLastFailMessage(StringPrintf(" return-wide on invalid register v%d", dec_insn.vA)); + work_line_->VerifyRegisterType(dec_insn.vA, return_type); + if (failure_ != VERIFY_ERROR_NONE) { + fail_messages_ << " return-wide on invalid register pair v" << dec_insn.vA; } } } break; case Instruction::RETURN_OBJECT: - if (!IsConstructor() || work_line_->CheckConstructorReturn()) { + if (!method_->IsConstructor() || work_line_->CheckConstructorReturn()) { const RegType& return_type = GetMethodReturnType(); if (!return_type.IsReferenceTypes()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-object not expected"; @@ -1448,9 +1426,9 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { // Get type from instruction if unresolved then we need an access check // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vB); - // Register holds class, ie its type is class, on error it will hold Conflict. + // Register holds class, ie its type is class, but on error we keep it Unknown work_line_->SetRegisterType(dec_insn.vA, - res_type.IsConflict() ? res_type : reg_types_.JavaLangClass()); + res_type.IsUnknown() ? res_type : reg_types_.JavaLangClass()); break; } case Instruction::MONITOR_ENTER: @@ -1493,9 +1471,9 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { bool is_checkcast = dec_insn.opcode == Instruction::CHECK_CAST; const RegType& res_type = ResolveClassAndCheckAccess(is_checkcast ? dec_insn.vB : dec_insn.vC); - if (res_type.IsConflict()) { - DCHECK_NE(failures_.size(), 0U); - break; // bad class + if (res_type.IsUnknown()) { + CHECK_NE(failure_, VERIFY_ERROR_NONE); + break; // couldn't resolve class } // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved const RegType& orig_type = @@ -1526,9 +1504,9 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } case Instruction::NEW_INSTANCE: { const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vB); - if (res_type.IsConflict()) { - DCHECK_NE(failures_.size(), 0U); - break; // bad class + if (res_type.IsUnknown()) { + CHECK_NE(failure_, VERIFY_ERROR_NONE); + break; // couldn't resolve class } // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved // can't create an instance of an interface or abstract class */ @@ -1612,8 +1590,9 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { if (!array_type.IsArrayTypes()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with array type " << array_type; } else { - const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_); - DCHECK(!component_type.IsConflict()); + const RegType& component_type = reg_types_.GetComponentType(array_type, + method_->GetDeclaringClass()->GetClassLoader()); + DCHECK(!component_type.IsUnknown()); if (component_type.IsNonZeroReferenceTypes()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with component type " << component_type; @@ -1827,25 +1806,28 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { bool is_super = (dec_insn.opcode == Instruction::INVOKE_SUPER || dec_insn.opcode == Instruction::INVOKE_SUPER_RANGE); Method* called_method = VerifyInvocationArgs(dec_insn, METHOD_VIRTUAL, is_range, is_super); - const char* descriptor; - if (called_method == NULL) { - uint32_t method_idx = dec_insn.vB; - const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); - uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; - descriptor = dex_file_->StringByTypeIdx(return_type_idx); - } else { - descriptor = MethodHelper(called_method).GetReturnTypeDescriptor(); + if (failure_ == VERIFY_ERROR_NONE) { + const char* descriptor; + if (called_method == NULL) { + uint32_t method_idx = dec_insn.vB; + const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); + uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; + descriptor = dex_file_->StringByTypeIdx(return_type_idx); + } else { + descriptor = MethodHelper(called_method).GetReturnTypeDescriptor(); + } + const RegType& return_type = + reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor); + work_line_->SetResultRegisterType(return_type); + just_set_result = true; } - const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor); - work_line_->SetResultRegisterType(return_type); - just_set_result = true; break; } case Instruction::INVOKE_DIRECT: case Instruction::INVOKE_DIRECT_RANGE: { bool is_range = (dec_insn.opcode == Instruction::INVOKE_DIRECT_RANGE); Method* called_method = VerifyInvocationArgs(dec_insn, METHOD_DIRECT, is_range, false); - if (called_method != NULL) { + if (failure_ == VERIFY_ERROR_NONE) { /* * Some additional checks when calling a constructor. We know from the invocation arg check * that the "this" argument is an instance of called_method->klass. Now we further restrict @@ -1853,9 +1835,18 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * allowing the latter only if the "this" argument is the same as the "this" argument to * this method (which implies that we're in a constructor ourselves). */ - if (called_method->IsConstructor()) { + bool is_constructor; + if (called_method != NULL) { + is_constructor = called_method->IsConstructor(); + } else { + uint32_t method_idx = dec_insn.vB; + const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); + const char* name = dex_file_->GetMethodName(method_id); + is_constructor = strcmp(name, "<init>") == 0; + } + if (is_constructor) { const RegType& this_type = work_line_->GetInvocationThis(dec_insn); - if (this_type.IsConflict()) // failure. + if (failure_ != VERIFY_ERROR_NONE) break; /* no null refs allowed (?) */ @@ -1864,29 +1855,18 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { break; } if (called_method != NULL) { + Class* this_class = this_type.GetClass(); + DCHECK(this_class != NULL); /* must be in same class or in superclass */ - const RegType& this_super_klass = this_type.GetSuperClass(®_types_); - if (this_super_klass.IsConflict()) { - // Unknown super class, fail so we re-check at runtime. - Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "super class unknown for '" << this_type << "'"; - break; - } else { - if (!this_super_klass.IsZero() && - called_method->GetDeclaringClass() == this_super_klass.GetClass()) { - if (this_type.GetClass() != GetDeclaringClass().GetClass()) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) - << "invoke-direct <init> on super only allowed for 'this' in <init>" - << " (this class '" << this_type << "', called class '" - << PrettyDescriptor(called_method->GetDeclaringClass()) << "')"; - break; - } - } else if (this_type.GetClass() != called_method->GetDeclaringClass()) { + if (called_method->GetDeclaringClass() == this_class->GetSuperClass()) { + if (this_class != method_->GetDeclaringClass()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) - << "invoke-direct <init> must be on current class or super" - << " (current class '" << this_type << "', called class '" - << PrettyDescriptor(called_method->GetDeclaringClass()) << "')"; + << "invoke-direct <init> on super only allowed for 'this' in <init>"; break; } + } else if (called_method->GetDeclaringClass() != this_class) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invoke-direct <init> must be on current class or super"; + break; } } @@ -1902,6 +1882,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * registers that have the same object instance in them, not just the "this" register. */ work_line_->MarkRefsAsInitialized(this_type); + if (failure_ != VERIFY_ERROR_NONE) + break; } const char* descriptor; if (called_method == NULL) { @@ -1912,7 +1894,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } else { descriptor = MethodHelper(called_method).GetReturnTypeDescriptor(); } - const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor); + const RegType& return_type = + reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor); work_line_->SetResultRegisterType(return_type); just_set_result = true; } @@ -1922,69 +1905,77 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::INVOKE_STATIC_RANGE: { bool is_range = (dec_insn.opcode == Instruction::INVOKE_STATIC_RANGE); Method* called_method = VerifyInvocationArgs(dec_insn, METHOD_STATIC, is_range, false); - const char* descriptor; - if (called_method == NULL) { - uint32_t method_idx = dec_insn.vB; - const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); - uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; - descriptor = dex_file_->StringByTypeIdx(return_type_idx); - } else { - descriptor = MethodHelper(called_method).GetReturnTypeDescriptor(); + if (failure_ == VERIFY_ERROR_NONE) { + const char* descriptor; + if (called_method == NULL) { + uint32_t method_idx = dec_insn.vB; + const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); + uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; + descriptor = dex_file_->StringByTypeIdx(return_type_idx); + } else { + descriptor = MethodHelper(called_method).GetReturnTypeDescriptor(); + } + const RegType& return_type = + reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor); + work_line_->SetResultRegisterType(return_type); + just_set_result = true; } - const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor); - work_line_->SetResultRegisterType(return_type); - just_set_result = true; } break; case Instruction::INVOKE_INTERFACE: case Instruction::INVOKE_INTERFACE_RANGE: { bool is_range = (dec_insn.opcode == Instruction::INVOKE_INTERFACE_RANGE); Method* abs_method = VerifyInvocationArgs(dec_insn, METHOD_INTERFACE, is_range, false); - if (abs_method != NULL) { - Class* called_interface = abs_method->GetDeclaringClass(); - if (!called_interface->IsInterface() && !called_interface->IsObjectClass()) { - Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected interface class in invoke-interface '" - << PrettyMethod(abs_method) << "'"; - break; + if (failure_ == VERIFY_ERROR_NONE) { + if (abs_method != NULL) { + Class* called_interface = abs_method->GetDeclaringClass(); + if (!called_interface->IsInterface() && !called_interface->IsObjectClass()) { + Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected interface class in invoke-interface '" + << PrettyMethod(abs_method) << "'"; + break; + } } - } - /* Get the type of the "this" arg, which should either be a sub-interface of called - * interface or Object (see comments in RegType::JoinClass). - */ - const RegType& this_type = work_line_->GetInvocationThis(dec_insn); - if (this_type.IsZero()) { - /* null pointer always passes (and always fails at runtime) */ - } else { - if (this_type.IsUninitializedTypes()) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface call on uninitialized object " - << this_type; - break; + /* Get the type of the "this" arg, which should either be a sub-interface of called + * interface or Object (see comments in RegType::JoinClass). + */ + const RegType& this_type = work_line_->GetInvocationThis(dec_insn); + if (failure_ == VERIFY_ERROR_NONE) { + if (this_type.IsZero()) { + /* null pointer always passes (and always fails at runtime) */ + } else { + if (this_type.IsUninitializedTypes()) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface call on uninitialized object " + << this_type; + break; + } + // In the past we have tried to assert that "called_interface" is assignable + // from "this_type.GetClass()", however, as we do an imprecise Join + // (RegType::JoinClass) we don't have full information on what interfaces are + // implemented by "this_type". For example, two classes may implement the same + // interfaces and have a common parent that doesn't implement the interface. The + // join will set "this_type" to the parent class and a test that this implements + // the interface will incorrectly fail. + } } - // In the past we have tried to assert that "called_interface" is assignable - // from "this_type.GetClass()", however, as we do an imprecise Join - // (RegType::JoinClass) we don't have full information on what interfaces are - // implemented by "this_type". For example, two classes may implement the same - // interfaces and have a common parent that doesn't implement the interface. The - // join will set "this_type" to the parent class and a test that this implements - // the interface will incorrectly fail. - } - /* - * We don't have an object instance, so we can't find the concrete method. However, all of - * the type information is in the abstract method, so we're good. - */ - const char* descriptor; - if (abs_method == NULL) { - uint32_t method_idx = dec_insn.vB; - const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); - uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; - descriptor = dex_file_->StringByTypeIdx(return_type_idx); - } else { - descriptor = MethodHelper(abs_method).GetReturnTypeDescriptor(); + /* + * We don't have an object instance, so we can't find the concrete method. However, all of + * the type information is in the abstract method, so we're good. + */ + const char* descriptor; + if (abs_method == NULL) { + uint32_t method_idx = dec_insn.vB; + const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); + uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; + descriptor = dex_file_->StringByTypeIdx(return_type_idx); + } else { + descriptor = MethodHelper(abs_method).GetReturnTypeDescriptor(); + } + const RegType& return_type = + reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor); + work_line_->SetResultRegisterType(return_type); + work_line_->SetResultRegisterType(return_type); + just_set_result = true; } - const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor); - work_line_->SetResultRegisterType(return_type); - work_line_->SetResultRegisterType(return_type); - just_set_result = true; break; } case Instruction::NEG_INT: @@ -2221,21 +2212,21 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { */ } // end - switch (dec_insn.opcode) - if (have_pending_hard_failure_) { - CHECK_EQ(failures_[failures_.size() - 1], VERIFY_ERROR_BAD_CLASS_HARD); - /* immediate failure, reject class */ - info_messages_ << "Rejecting opcode " << inst->DumpString(dex_file_); - return false; - } else if (have_pending_rewrite_failure_) { - /* replace opcode and continue on */ - std::string append("Replacing opcode "); - append += inst->DumpString(dex_file_); - AppendToLastFailMessage(append); - ReplaceFailingInstruction(); - /* IMPORTANT: method->insns may have been changed */ - insns = code_item_->insns_ + work_insn_idx_; - /* continue on as if we just handled a throw-verification-error */ - opcode_flags = Instruction::kThrow; + if (failure_ != VERIFY_ERROR_NONE) { + if (failure_ == VERIFY_ERROR_BAD_CLASS_HARD || failure_ == VERIFY_ERROR_BAD_CLASS_SOFT) { + /* immediate failure, reject class */ + fail_messages_ << std::endl << "Rejecting opcode " << inst->DumpString(dex_file_); + return false; + } else { + /* replace opcode and continue on */ + fail_messages_ << std::endl << "Replacing opcode " << inst->DumpString(dex_file_); + ReplaceFailingInstruction(); + /* IMPORTANT: method->insns may have been changed */ + insns = code_item_->insns_ + work_insn_idx_; + /* continue on as if we just handled a throw-verification-error */ + failure_ = VERIFY_ERROR_NONE; + opcode_flags = Instruction::kThrow; + } } /* * If we didn't just set the result register, clear it out. This ensures that you can only use @@ -2410,25 +2401,25 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { const RegType& MethodVerifier::ResolveClassAndCheckAccess(uint32_t class_idx) { const char* descriptor = dex_file_->StringByTypeIdx(class_idx); - const RegType& referrer = GetDeclaringClass(); - Class* klass = dex_cache_->GetResolvedType(class_idx); + Class* referrer = method_->GetDeclaringClass(); + Class* klass = method_->GetDexCacheResolvedTypes()->Get(class_idx); const RegType& result = klass != NULL ? reg_types_.FromClass(klass) - : reg_types_.FromDescriptor(class_loader_, descriptor); - if (result.IsConflict()) { - Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "accessing broken descriptor '" << descriptor - << "' in " << referrer; - return result; - } + : reg_types_.FromDescriptor(referrer->GetClassLoader(), descriptor); if (klass == NULL && !result.IsUnresolvedTypes()) { - dex_cache_->SetResolvedType(class_idx, result.GetClass()); + method_->GetDexCacheResolvedTypes()->Set(class_idx, result.GetClass()); } - // Check if access is allowed. Unresolved types use xxxWithAccessCheck to + if (result.IsUnknown()) { + Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "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.IsUnresolvedTypes() && !referrer.CanAccess(result)) { + if (!result.IsUnresolvedTypes() && !referrer->CanAccess(result.GetClass())) { Fail(VERIFY_ERROR_ACCESS_CLASS) << "illegal class access: '" - << referrer << "' -> '" << result << "'"; - return reg_types_.Conflict(); + << PrettyDescriptor(referrer) << "' -> '" + << result << "'"; + return reg_types_.Unknown(); } else { return result; } @@ -2455,7 +2446,7 @@ const RegType& MethodVerifier::GetCaughtExceptionType() { // We don't know enough about the type and the common path merge will result in // Conflict. Fail here knowing the correct thing can be done at runtime. Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "unexpected non-exception class " << exception; - return reg_types_.Conflict(); + return reg_types_.Unknown(); } else if (common_super->Equals(exception)) { // odd case, but nothing to do } else { @@ -2471,26 +2462,25 @@ const RegType& MethodVerifier::GetCaughtExceptionType() { if (common_super == NULL) { /* no catch blocks, or no catches with classes we can find */ Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "unable to find exception handler"; - return reg_types_.Conflict(); + return reg_types_.Unknown(); } return *common_super; } -Method* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t dex_method_idx, MethodType method_type) { - const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx); +Method* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t method_idx, MethodType method_type) { + const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); const RegType& klass_type = ResolveClassAndCheckAccess(method_id.class_idx_); - if (klass_type.IsConflict()) { - std::string append(" in attempt to access method "); - append += dex_file_->GetMethodName(method_id); - AppendToLastFailMessage(append); + if (failure_ != VERIFY_ERROR_NONE) { + fail_messages_ << " in attempt to access method " << dex_file_->GetMethodName(method_id); return NULL; } if (klass_type.IsUnresolvedTypes()) { return NULL; // Can't resolve Class so no more to do here } Class* klass = klass_type.GetClass(); - const RegType& referrer = GetDeclaringClass(); - Method* res_method = dex_cache_->GetResolvedMethod(dex_method_idx); + Class* referrer = method_->GetDeclaringClass(); + DexCache* dex_cache = referrer->GetDexCache(); + Method* res_method = dex_cache->GetResolvedMethod(method_idx); if (res_method == NULL) { const char* name = dex_file_->GetMethodName(method_id); std::string signature(dex_file_->CreateMethodSignature(method_id.proto_idx_, NULL)); @@ -2503,7 +2493,7 @@ Method* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t dex_method_idx, Met res_method = klass->FindVirtualMethod(name, signature); } if (res_method != NULL) { - dex_cache_->SetResolvedMethod(dex_method_idx, res_method); + dex_cache->SetResolvedMethod(method_idx, res_method); } else { // If a virtual or interface method wasn't found with the expected type, look in // the direct methods. This can happen when the wrong invoke type is used or when @@ -2533,9 +2523,9 @@ Method* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t dex_method_idx, Met return NULL; } // Check if access is allowed. - if (!referrer.CanAccessMember(res_method->GetDeclaringClass(), res_method->GetAccessFlags())) { + if (!referrer->CanAccessMember(res_method->GetDeclaringClass(), res_method->GetAccessFlags())) { Fail(VERIFY_ERROR_ACCESS_METHOD) << "illegal method access (call " << PrettyMethod(res_method) - << " from " << referrer << ")"; + << " from " << PrettyDescriptor(referrer) << ")"; return NULL; } // Check that invoke-virtual and invoke-super are not used on private methods of the same class. @@ -2580,18 +2570,15 @@ Method* MethodVerifier::VerifyInvocationArgs(const DecodedInstruction& dec_insn, // has a vtable entry for the target method. if (is_super) { DCHECK(method_type == METHOD_VIRTUAL); - const RegType& super = GetDeclaringClass().GetSuperClass(®_types_); - Class* super_klass = super.GetClass(); - if (res_method->GetMethodIndex() >= super_klass->GetVTable()->GetLength()) { - if (super.IsConflict()) { // Only Object has no super class - Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from " - << PrettyMethod(method_idx_, *dex_file_) + Class* super = method_->GetDeclaringClass()->GetSuperClass(); + if (super == NULL || res_method->GetMethodIndex() >= super->GetVTable()->GetLength()) { + if (super == NULL) { // Only Object has no super class + Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from " << PrettyMethod(method_) << " to super " << PrettyMethod(res_method); } else { MethodHelper mh(res_method); - Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from " - << PrettyMethod(method_idx_, *dex_file_) - << " to super " << super + Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from " << PrettyMethod(method_) + << " to super " << PrettyDescriptor(super) << "." << mh.GetName() << mh.GetSignature(); } @@ -2611,14 +2598,15 @@ Method* MethodVerifier::VerifyInvocationArgs(const DecodedInstruction& dec_insn, } /* - * Check the "this" argument, which must be an instance of the class that declared the method. - * For an interface class, we don't do the full interface merge (see JoinClass), so we can't do a - * rigorous check here (which is okay since we have to do it at runtime). + * Check the "this" argument, which must be an instance of the class + * that declared the method. For an interface class, we don't do the + * full interface merge, so we can't do a rigorous check here (which + * is okay since we have to do it at runtime). */ size_t actual_args = 0; if (!res_method->IsStatic()) { const RegType& actual_arg_type = work_line_->GetInvocationThis(dec_insn); - if (actual_arg_type.IsConflict()) { // GetInvocationThis failed. + if (failure_ != VERIFY_ERROR_NONE) { return NULL; } if (actual_arg_type.IsUninitializedReference() && !res_method->IsConstructor()) { @@ -2656,7 +2644,8 @@ Method* MethodVerifier::VerifyInvocationArgs(const DecodedInstruction& dec_insn, << " missing signature component"; return NULL; } - const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor); + const RegType& reg_type = + reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor); uint32_t get_reg = is_range ? dec_insn.vC + actual_args : dec_insn.arg[actual_args]; if (!work_line_->VerifyRegisterType(get_reg, reg_type)) { return NULL; @@ -2672,11 +2661,16 @@ Method* MethodVerifier::VerifyInvocationArgs(const DecodedInstruction& dec_insn, } } +const RegType& MethodVerifier::GetMethodReturnType() { + return reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), + MethodHelper(method_).GetReturnTypeDescriptor()); +} + void MethodVerifier::VerifyNewArray(const DecodedInstruction& dec_insn, bool is_filled, bool is_range) { const RegType& res_type = ResolveClassAndCheckAccess(is_filled ? dec_insn.vB : dec_insn.vC); - if (res_type.IsConflict()) { // bad class - DCHECK_NE(failures_.size(), 0U); + if (res_type.IsUnknown()) { + CHECK_NE(failure_, VERIFY_ERROR_NONE); } else { // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved if (!res_type.IsArrayTypes()) { @@ -2689,12 +2683,13 @@ void MethodVerifier::VerifyNewArray(const DecodedInstruction& dec_insn, bool is_ } else { // Verify each register. If "arg_count" is bad, VerifyRegisterType() will run off the end of // the list and fail. It's legal, if silly, for arg_count to be zero. - const RegType& expected_type = reg_types_.GetComponentType(res_type, class_loader_); + const RegType& expected_type = reg_types_.GetComponentType(res_type, + method_->GetDeclaringClass()->GetClassLoader()); uint32_t arg_count = dec_insn.vA; for (size_t ui = 0; ui < arg_count; ui++) { uint32_t get_reg = is_range ? dec_insn.vC + ui : dec_insn.arg[ui]; if (!work_line_->VerifyRegisterType(get_reg, expected_type)) { - work_line_->SetResultRegisterType(reg_types_.Conflict()); + work_line_->SetResultRegisterType(reg_types_.Unknown()); return; } } @@ -2725,7 +2720,8 @@ void MethodVerifier::VerifyAGet(const DecodedInstruction& dec_insn, Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aget"; } else { /* verify the class */ - const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_); + const RegType& component_type = reg_types_.GetComponentType(array_type, + method_->GetDeclaringClass()->GetClassLoader()); if (!component_type.IsReferenceTypes() && !is_primitive) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "primitive array type " << array_type << " source for aget-object"; @@ -2761,7 +2757,8 @@ void MethodVerifier::VerifyAPut(const DecodedInstruction& dec_insn, Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aput"; } else { /* verify the class */ - const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_); + const RegType& component_type = reg_types_.GetComponentType(array_type, + method_->GetDeclaringClass()->GetClassLoader()); if (!component_type.IsReferenceTypes() && !is_primitive) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "primitive array type " << array_type << " source for aput-object"; @@ -2787,17 +2784,16 @@ Field* MethodVerifier::GetStaticField(int field_idx) { const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx); // Check access to class const RegType& klass_type = ResolveClassAndCheckAccess(field_id.class_idx_); - if (klass_type.IsConflict()) { // bad class - AppendToLastFailMessage(StringPrintf(" in attempt to access static field %d (%s) in %s", - field_idx, dex_file_->GetFieldName(field_id), - dex_file_->GetFieldDeclaringClassDescriptor(field_id))); + if (failure_ != VERIFY_ERROR_NONE) { + fail_messages_ << " in attempt to access static field " << field_idx << " (" + << dex_file_->GetFieldName(field_id) << ") in " + << dex_file_->GetFieldDeclaringClassDescriptor(field_id); return NULL; } if (klass_type.IsUnresolvedTypes()) { - return NULL; // Can't resolve Class so no more to do here, will do checking at runtime. + return NULL; // Can't resolve Class so no more to do here } - Field* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(*dex_file_, field_idx, - dex_cache_, class_loader_); + Field* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(field_idx, method_); if (field == NULL) { LOG(INFO) << "unable to resolve static field " << field_idx << " (" << dex_file_->GetFieldName(field_id) << ") in " @@ -2805,10 +2801,10 @@ Field* MethodVerifier::GetStaticField(int field_idx) { DCHECK(Thread::Current()->IsExceptionPending()); Thread::Current()->ClearException(); return NULL; - } else if (!GetDeclaringClass().CanAccessMember(field->GetDeclaringClass(), - field->GetAccessFlags())) { + } else if (!method_->GetDeclaringClass()->CanAccessMember(field->GetDeclaringClass(), + field->GetAccessFlags())) { Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot access static field " << PrettyField(field) - << " from " << GetDeclaringClass(); + << " from " << PrettyClass(method_->GetDeclaringClass()); return NULL; } else if (!field->IsStatic()) { Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected field " << PrettyField(field) << " to be static"; @@ -2822,17 +2818,16 @@ Field* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_idx) const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx); // Check access to class const RegType& klass_type = ResolveClassAndCheckAccess(field_id.class_idx_); - if (klass_type.IsConflict()) { - AppendToLastFailMessage(StringPrintf(" in attempt to access instance field %d (%s) in %s", - field_idx, dex_file_->GetFieldName(field_id), - dex_file_->GetFieldDeclaringClassDescriptor(field_id))); + if (failure_ != VERIFY_ERROR_NONE) { + fail_messages_ << " in attempt to access instance field " << field_idx << " (" + << dex_file_->GetFieldName(field_id) << ") in " + << dex_file_->GetFieldDeclaringClassDescriptor(field_id); return NULL; } if (klass_type.IsUnresolvedTypes()) { return NULL; // Can't resolve Class so no more to do here } - Field* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(*dex_file_, field_idx, - dex_cache_, class_loader_); + Field* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(field_idx, method_); if (field == NULL) { LOG(INFO) << "unable to resolve instance field " << field_idx << " (" << dex_file_->GetFieldName(field_id) << ") in " @@ -2840,10 +2835,10 @@ Field* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_idx) DCHECK(Thread::Current()->IsExceptionPending()); Thread::Current()->ClearException(); return NULL; - } else if (!GetDeclaringClass().CanAccessMember(field->GetDeclaringClass(), - field->GetAccessFlags())) { + } else if (!method_->GetDeclaringClass()->CanAccessMember(field->GetDeclaringClass(), + field->GetAccessFlags())) { Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot access instance field " << PrettyField(field) - << " from " << GetDeclaringClass(); + << " from " << PrettyClass(method_->GetDeclaringClass()); return NULL; } else if (field->IsStatic()) { Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected field " << PrettyField(field) @@ -2852,27 +2847,24 @@ Field* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_idx) } else if (obj_type.IsZero()) { // Cannot infer and check type, however, access will cause null pointer exception return field; - } else { - const RegType& field_klass = reg_types_.FromClass(field->GetDeclaringClass()); - if (obj_type.IsUninitializedTypes() && - (!IsConstructor() || GetDeclaringClass().Equals(obj_type) || - !field_klass.Equals(GetDeclaringClass()))) { - // Field accesses through uninitialized references are only allowable for constructors where - // the field is declared in this class - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "cannot access instance field " << PrettyField(field) + } else if (obj_type.IsUninitializedTypes() && + (!method_->IsConstructor() || method_->GetDeclaringClass() != obj_type.GetClass() || + field->GetDeclaringClass() != method_->GetDeclaringClass())) { + // Field accesses through uninitialized references are only allowable for constructors where + // the field is declared in this class + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "cannot access instance field " << PrettyField(field) << " of a not fully initialized object within the context of " - << PrettyMethod(method_idx_, *dex_file_); - return NULL; - } else if (!field_klass.IsAssignableFrom(obj_type)) { - // Trying to access C1.field1 using reference of type C2, which is neither C1 or a sub-class - // of C1. For resolution to occur the declared class of the field must be compatible with - // obj_type, we've discovered this wasn't so, so report the field didn't exist. - Fail(VERIFY_ERROR_NO_FIELD) << "cannot access instance field " << PrettyField(field) - << " from object of type " << obj_type; - return NULL; - } else { - return field; - } + << PrettyMethod(method_); + return NULL; + } else if (!field->GetDeclaringClass()->IsAssignableFrom(obj_type.GetClass())) { + // Trying to access C1.field1 using reference of type C2, which is neither C1 or a sub-class + // of C1. For resolution to occur the declared class of the field must be compatible with + // obj_type, we've discovered this wasn't so, so report the field didn't exist. + Fail(VERIFY_ERROR_NO_FIELD) << "cannot access instance field " << PrettyField(field) + << " from object of type " << PrettyClass(obj_type.GetClass()); + return NULL; + } else { + return field; } } @@ -2886,44 +2878,46 @@ void MethodVerifier::VerifyISGet(const DecodedInstruction& dec_insn, const RegType& object_type = work_line_->GetRegisterType(dec_insn.vB); field = GetInstanceField(object_type, field_idx); } - const char* descriptor; - const ClassLoader* loader; - if (field != NULL) { - descriptor = FieldHelper(field).GetTypeDescriptor(); - loader = field->GetDeclaringClass()->GetClassLoader(); + if (failure_ != VERIFY_ERROR_NONE) { + work_line_->SetRegisterType(dec_insn.vA, reg_types_.Unknown()); } else { - const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx); - descriptor = dex_file_->GetFieldTypeDescriptor(field_id); - loader = class_loader_; - } - const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor); - if (is_primitive) { - if (field_type.Equals(insn_type) || - (field_type.IsFloat() && insn_type.IsIntegralTypes()) || - (field_type.IsDouble() && insn_type.IsLongTypes())) { - // expected that read is of the correct primitive type or that int reads are reading - // floats or long reads are reading doubles + const char* descriptor; + const ClassLoader* loader; + if (field != NULL) { + descriptor = FieldHelper(field).GetTypeDescriptor(); + loader = field->GetDeclaringClass()->GetClassLoader(); } else { - // This is a global failure rather than a class change failure as the instructions and - // the descriptors for the type should have been consistent within the same file at - // compile time - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field) - << " to be of type '" << insn_type - << "' but found type '" << field_type << "' in get"; - work_line_->SetRegisterType(dec_insn.vA, reg_types_.Conflict()); - return; - } - } else { - if (!insn_type.IsAssignableFrom(field_type)) { - Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field) - << " to be compatible with type '" << insn_type - << "' but found type '" << field_type - << "' in get-object"; - work_line_->SetRegisterType(dec_insn.vA, reg_types_.Conflict()); - return; + const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx); + descriptor = dex_file_->GetFieldTypeDescriptor(field_id); + loader = method_->GetDeclaringClass()->GetClassLoader(); + } + const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor); + if (is_primitive) { + if (field_type.Equals(insn_type) || + (field_type.IsFloat() && insn_type.IsIntegralTypes()) || + (field_type.IsDouble() && insn_type.IsLongTypes())) { + // expected that read is of the correct primitive type or that int reads are reading + // floats or long reads are reading doubles + } else { + // This is a global failure rather than a class change failure as the instructions and + // the descriptors for the type should have been consistent within the same file at + // compile time + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field) + << " to be of type '" << insn_type + << "' but found type '" << field_type << "' in get"; + return; + } + } else { + if (!insn_type.IsAssignableFrom(field_type)) { + Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field) + << " to be compatible with type '" << insn_type + << "' but found type '" << field_type + << "' in get-object"; + return; + } } + work_line_->SetRegisterType(dec_insn.vA, field_type); } - work_line_->SetRegisterType(dec_insn.vA, field_type); } void MethodVerifier::VerifyISPut(const DecodedInstruction& dec_insn, @@ -2936,71 +2930,75 @@ void MethodVerifier::VerifyISPut(const DecodedInstruction& dec_insn, const RegType& object_type = work_line_->GetRegisterType(dec_insn.vB); field = GetInstanceField(object_type, field_idx); } - const char* descriptor; - const ClassLoader* loader; - if (field != NULL) { - descriptor = FieldHelper(field).GetTypeDescriptor(); - loader = field->GetDeclaringClass()->GetClassLoader(); + if (failure_ != VERIFY_ERROR_NONE) { + work_line_->SetRegisterType(dec_insn.vA, reg_types_.Unknown()); } else { - const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx); - descriptor = dex_file_->GetFieldTypeDescriptor(field_id); - loader = class_loader_; - } - const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor); - if (field != NULL) { - if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) { - Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field) - << " from other class " << GetDeclaringClass(); - return; - } - } - if (is_primitive) { - // Primitive field assignability rules are weaker than regular assignability rules - bool instruction_compatible; - bool value_compatible; - const RegType& value_type = work_line_->GetRegisterType(dec_insn.vA); - if (field_type.IsIntegralTypes()) { - instruction_compatible = insn_type.IsIntegralTypes(); - value_compatible = value_type.IsIntegralTypes(); - } else if (field_type.IsFloat()) { - instruction_compatible = insn_type.IsInteger(); // no [is]put-float, so expect [is]put-int - value_compatible = value_type.IsFloatTypes(); - } else if (field_type.IsLong()) { - instruction_compatible = insn_type.IsLong(); - value_compatible = value_type.IsLongTypes(); - } else if (field_type.IsDouble()) { - instruction_compatible = insn_type.IsLong(); // no [is]put-double, so expect [is]put-long - value_compatible = value_type.IsDoubleTypes(); + const char* descriptor; + const ClassLoader* loader; + if (field != NULL) { + descriptor = FieldHelper(field).GetTypeDescriptor(); + loader = field->GetDeclaringClass()->GetClassLoader(); } else { - instruction_compatible = false; // reference field with primitive store - value_compatible = false; // unused - } - if (!instruction_compatible) { - // This is a global failure rather than a class change failure as the instructions and - // the descriptors for the type should have been consistent within the same file at - // compile time - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field) - << " to be of type '" << insn_type - << "' but found type '" << field_type - << "' in put"; - return; - } - if (!value_compatible) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected value in v" << dec_insn.vA - << " of type " << value_type - << " but expected " << field_type - << " for store to " << PrettyField(field) << " in put"; - return; + const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx); + descriptor = dex_file_->GetFieldTypeDescriptor(field_id); + loader = method_->GetDeclaringClass()->GetClassLoader(); + } + const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor); + if (field != NULL) { + if (field->IsFinal() && field->GetDeclaringClass() != method_->GetDeclaringClass()) { + Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field) + << " from other class " << PrettyClass(method_->GetDeclaringClass()); + return; + } } - } else { - if (!insn_type.IsAssignableFrom(field_type)) { - Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field) - << " to be compatible with type '" << insn_type - << "' but found type '" << field_type - << "' in put-object"; - return; + if (is_primitive) { + // Primitive field assignability rules are weaker than regular assignability rules + bool instruction_compatible; + bool value_compatible; + const RegType& value_type = work_line_->GetRegisterType(dec_insn.vA); + if (field_type.IsIntegralTypes()) { + instruction_compatible = insn_type.IsIntegralTypes(); + value_compatible = value_type.IsIntegralTypes(); + } else if (field_type.IsFloat()) { + instruction_compatible = insn_type.IsInteger(); // no [is]put-float, so expect [is]put-int + value_compatible = value_type.IsFloatTypes(); + } else if (field_type.IsLong()) { + instruction_compatible = insn_type.IsLong(); + value_compatible = value_type.IsLongTypes(); + } else if (field_type.IsDouble()) { + instruction_compatible = insn_type.IsLong(); // no [is]put-double, so expect [is]put-long + value_compatible = value_type.IsDoubleTypes(); + } else { + instruction_compatible = false; // reference field with primitive store + value_compatible = false; // unused + } + if (!instruction_compatible) { + // This is a global failure rather than a class change failure as the instructions and + // the descriptors for the type should have been consistent within the same file at + // compile time + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field) + << " to be of type '" << insn_type + << "' but found type '" << field_type + << "' in put"; + return; + } + if (!value_compatible) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected value in v" << dec_insn.vA + << " of type " << value_type + << " but expected " << field_type + << " for store to " << PrettyField(field) << " in put"; + return; + } + } else { + if (!insn_type.IsAssignableFrom(field_type)) { + Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field) + << " to be compatible with type '" << insn_type + << "' but found type '" << field_type + << "' in put-object"; + return; + } + work_line_->VerifyRegisterType(dec_insn.vA, field_type); } - work_line_->VerifyRegisterType(dec_insn.vA, field_type); } } @@ -3013,19 +3011,9 @@ bool MethodVerifier::CheckNotMoveException(const uint16_t* insns, int insn_idx) } void MethodVerifier::ReplaceFailingInstruction() { - // Pop the failure and clear the need for rewriting. - size_t failure_number = failures_.size(); - CHECK_NE(failure_number, 0U); - DCHECK_EQ(failure_messages_.size(), failure_number); - std::ostringstream* failure_message = failure_messages_[failure_number - 1]; - VerifyError failure = failures_[failure_number - 1]; - failures_.pop_back(); - failure_messages_.pop_back(); - have_pending_rewrite_failure_ = false; - if (Runtime::Current()->IsStarted()) { - LOG(ERROR) << "Verification attempting to replace instructions at runtime in " - << PrettyMethod(method_idx_, *dex_file_) << " " << failure_message->str(); + LOG(ERROR) << "Verification attempting to replace instructions in " << PrettyMethod(method_) + << " " << fail_messages_.str(); return; } const Instruction* inst = Instruction::At(code_item_->insns_ + work_insn_idx_); @@ -3099,14 +3087,13 @@ void MethodVerifier::ReplaceFailingInstruction() { } // Encode the opcode, with the failure code in the high byte uint16_t new_instruction = Instruction::THROW_VERIFICATION_ERROR | - (failure << 8) | // AA - component + (failure_ << 8) | // AA - component (ref_type << (8 + kVerifyErrorRefTypeShift)); insns[work_insn_idx_] = new_instruction; // The 2nd code unit (higher in memory) with the reference in, comes from the instruction we // rewrote, so nothing to do here. - LOG(INFO) << "Verification error, replacing instructions in " - << PrettyMethod(method_idx_, *dex_file_) << " " - << failure_message->str(); + LOG(INFO) << "Verification error, replacing instructions in " << PrettyMethod(method_) << " " + << fail_messages_.str(); if (gDebugVerify) { std::cout << std::endl << info_messages_.str(); Dump(std::cout); @@ -3129,7 +3116,7 @@ bool MethodVerifier::UpdateRegisters(uint32_t next_insn, const RegisterLine* mer copy->CopyFromLine(target_line); } changed = target_line->MergeRegisters(merge_line); - if (have_pending_hard_failure_) { + if (failure_ != VERIFY_ERROR_NONE) { return false; } if (gDebugVerify && changed) { @@ -3150,24 +3137,6 @@ InsnFlags* MethodVerifier::CurrentInsnFlags() { return &insn_flags_[work_insn_idx_]; } -const RegType& MethodVerifier::GetMethodReturnType() { - const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx_); - const DexFile::ProtoId& proto_id = dex_file_->GetMethodPrototype(method_id); - uint16_t return_type_idx = proto_id.return_type_idx_; - const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(return_type_idx)); - return reg_types_.FromDescriptor(class_loader_, descriptor); -} - -const RegType& MethodVerifier::GetDeclaringClass() { - if (foo_method_ != NULL) { - return reg_types_.FromClass(foo_method_->GetDeclaringClass()); - } else { - const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx_); - const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_)); - return reg_types_.FromDescriptor(class_loader_, descriptor); - } -} - void MethodVerifier::ComputeGcMapSizes(size_t* gc_points, size_t* ref_bitmap_bits, size_t* log2_max_gc_pc) { size_t local_gc_points = 0; diff --git a/src/verifier/method_verifier.h b/src/verifier/method_verifier.h index 512e26f21e..fc5b7eae57 100644 --- a/src/verifier/method_verifier.h +++ b/src/verifier/method_verifier.h @@ -83,6 +83,7 @@ enum MethodType { * to be rewritten to fail at runtime. */ enum VerifyError { + VERIFY_ERROR_NONE = 0, // No error; must be zero. VERIFY_ERROR_BAD_CLASS_HARD, // VerifyError; hard error that skips compilation. VERIFY_ERROR_BAD_CLASS_SOFT, // VerifyError; soft error that verifies again at runtime. @@ -152,6 +153,11 @@ class MethodVerifier { public: /* Verify a class. Returns "true" on success. */ static bool VerifyClass(const Class* klass, std::string& error); + + /* + * Structurally verify a class. Returns "true" on success. Used at compile time + * when the pointer for the method or declaring class can't be resolved. + */ static bool VerifyClass(const DexFile* dex_file, DexCache* dex_cache, const ClassLoader* class_loader, uint32_t class_def_idx, std::string& error); @@ -165,18 +171,15 @@ class MethodVerifier { return ®_types_; } - // Log a verification failure. + // Verification failed std::ostream& Fail(VerifyError error); - // Log for verification information. + // Log for verification information std::ostream& LogVerifyInfo() { - return info_messages_ << "VFY: " << PrettyMethod(method_idx_, *dex_file_) + return info_messages_ << "VFY: " << PrettyMethod(method_) << '[' << reinterpret_cast<void*>(work_insn_idx_) << "] : "; } - // Dump the failures encountered by the verifier. - std::ostream& DumpFailures(std::ostream& os); - // Dump the state of the verifier, namely each instruction, what flags are set on it, register // information void Dump(std::ostream& os); @@ -195,15 +198,9 @@ class MethodVerifier { private: + explicit MethodVerifier(Method* method); explicit MethodVerifier(const DexFile* dex_file, DexCache* dex_cache, - const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item, - uint32_t method_idx, Method* method, uint32_t access_flags); - - // Adds the given string to the beginning of the last failure message. - void PrependToLastFailMessage(std::string); - - // Adds the given string to the end of the last failure message. - void AppendToLastFailMessage(std::string); + const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item); /* * Perform verification on a single method. @@ -215,15 +212,42 @@ class MethodVerifier { * operands. * (3) Iterate through the method, checking type safety and looking * for code flow problems. + * + * Some checks may be bypassed depending on the verification mode. We can't + * turn this stuff off completely if we want to do "exact" GC. + * + * Confirmed here: + * - code array must not be empty + * Confirmed by ComputeWidthsAndCountOps(): + * - opcode of first instruction begins at index 0 + * - only documented instructions may appear + * - each instruction follows the last + * - last byte of last instruction is at (code_length-1) */ - static bool VerifyMethod(uint32_t method_idx, const DexFile* dex_file, DexCache* dex_cache, - const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item, - Method* method, uint32_t method_access_flags); + static bool VerifyMethod(Method* method); static void VerifyMethodAndDump(Method* method); - // Run verification on the method. Returns true if verification completes and false if the input - // has an irrecoverable corruption. - bool Verify(); + /* + * Perform structural verification on a single method. Used at compile time + * when the pointer for the method or declaring class can't be resolved. + * + * We do this in two passes: + * (1) Walk through all code units, determining instruction locations, + * widths, and other characteristics. + * (2) Walk through all code units, performing static checks on + * operands. + * + * Code flow verification is skipped since a resolved method and class are + * necessary to perform all the checks. + */ + static bool VerifyMethod(uint32_t method_idx, const DexFile* dex_file, DexCache* dex_cache, + const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item); + + /* Run both structural and code flow verification on the method. */ + bool VerifyAll(); + + /* Perform structural verification on a single method. */ + bool VerifyStructure(); /* * Compute the width of the instruction at each address in the instruction stream, and store it in @@ -495,6 +519,13 @@ class MethodVerifier { MethodType method_type, bool is_range, bool is_super); /* + * Return the register type for the method. We can't just use the already-computed + * DalvikJniReturnType, because if it's a reference type we need to do the class lookup. + * Returned references are assumed to be initialized. Returns kRegTypeUnknown for "void". + */ + const RegType& GetMethodReturnType(); + + /* * Verify that the target instruction is not "move-exception". It's important that the only way * to execute a move-exception is as the first instruction of an exception handler. * Returns "true" if all is well, "false" if the target instruction is move-exception. @@ -514,22 +545,6 @@ class MethodVerifier { */ bool UpdateRegisters(uint32_t next_insn, const RegisterLine* merge_line); - // Is the method being verified a constructor? - bool IsConstructor() const { - return (method_access_flags_ & kAccConstructor) != 0; - } - - // Is the method verified static? - bool IsStatic() const { - return (method_access_flags_ & kAccStatic) != 0; - } - - // Return the register type for the method. - const RegType& GetMethodReturnType(); - - // Get a type representing the declaring class of the method. - const RegType& GetDeclaringClass(); - #if defined(ART_USE_LLVM_COMPILER) /* * Generate the inferred register category for LLVM-based code generator. @@ -586,9 +601,7 @@ class MethodVerifier { // Storage for the register status we're saving for later. UniquePtr<RegisterLine> saved_line_; - uint32_t method_idx_; // The method we're working on. - Method* foo_method_; // Its object representation if known. - uint32_t method_access_flags_; // Method's access flags. + Method* method_; // The method we're working on. const DexFile* dex_file_; // The dex file containing the method. DexCache* dex_cache_; // The dex_cache for the declaring class of the method. const ClassLoader* class_loader_; // The class loader for the declaring class of the method. @@ -596,16 +609,12 @@ class MethodVerifier { const DexFile::CodeItem* code_item_; // The code item containing the code for the method. UniquePtr<InsnFlags[]> insn_flags_; // Instruction widths and flags, one entry per code unit. - // The types of any error that occurs. - std::vector<VerifyError> failures_; - // Error messages associated with failures. - std::vector<std::ostringstream*> failure_messages_; - // Is there a pending hard failure? - bool have_pending_hard_failure_; - // Is there a pending failure that will cause dex opcodes to be rewritten. - bool have_pending_rewrite_failure_; + // The type of any error that occurs + VerifyError failure_; - // Info message log use primarily for verifier diagnostics. + // Failure message log + std::ostringstream fail_messages_; + // Info message log std::ostringstream info_messages_; // The number of occurrences of specific opcodes. diff --git a/src/verifier/reg_type.cc b/src/verifier/reg_type.cc index 6598458fc7..f72ad6ab13 100644 --- a/src/verifier/reg_type.cc +++ b/src/verifier/reg_type.cc @@ -23,7 +23,7 @@ namespace art { namespace verifier { static const char* type_strings[] = { - "Undefined", + "Unknown", "Conflict", "Boolean", "Byte", @@ -42,12 +42,11 @@ static const char* type_strings[] = { "Uninitialized Reference", "Uninitialized This Reference", "Unresolved And Uninitialized Reference", - "Unresolved And Uninitialized This Reference", "Reference", }; std::string RegType::Dump() const { - DCHECK(type_ >= kRegTypeUndefined && type_ <= kRegTypeReference); + DCHECK(type_ >= kRegTypeUnknown && type_ <= kRegTypeReference); std::string result; if (IsConstant()) { uint32_t val = ConstantValue(); @@ -85,44 +84,69 @@ const RegType& RegType::HighHalf(RegTypeCache* cache) const { } } -const RegType& RegType::GetSuperClass(RegTypeCache* cache) const { - if (!IsUnresolvedTypes()) { - Class* super_klass = GetClass()->GetSuperClass(); - if (super_klass != NULL) { - return cache->FromClass(super_klass); - } else { - return cache->Zero(); +/* + * A basic Join operation on classes. For a pair of types S and T the Join, written S v T = J, is + * S <: J, T <: J and for-all U such that S <: U, T <: U then J <: U. That is J is the parent of + * S and T such that there isn't a parent of both S and T that isn't also the parent of J (ie J + * is the deepest (lowest upper bound) parent of S and T). + * + * This operation applies for regular classes and arrays, however, for interface types there needn't + * be a partial ordering on the types. We could solve the problem of a lack of a partial order by + * introducing sets of types, however, the only operation permissible on an interface is + * invoke-interface. In the tradition of Java verifiers we defer the verification of interface + * types until an invoke-interface call on the interface typed reference at runtime and allow + * the perversion of any Object being assignable to an interface type (note, however, that we don't + * allow assignment of Object or Interface to any concrete subclass of Object and are therefore type + * safe; further the Join on a Object cannot result in a sub-class by definition). + */ +Class* RegType::ClassJoin(Class* s, Class* t) { + DCHECK(!s->IsPrimitive()) << PrettyClass(s); + DCHECK(!t->IsPrimitive()) << PrettyClass(t); + if (s == t) { + return s; + } else if (s->IsAssignableFrom(t)) { + return s; + } else if (t->IsAssignableFrom(s)) { + return t; + } else if (s->IsArrayClass() && t->IsArrayClass()) { + Class* s_ct = s->GetComponentType(); + Class* t_ct = t->GetComponentType(); + if (s_ct->IsPrimitive() || t_ct->IsPrimitive()) { + // Given the types aren't the same, if either array is of primitive types then the only + // common parent is java.lang.Object + Class* result = s->GetSuperClass(); // short-cut to java.lang.Object + DCHECK(result->IsObjectClass()); + return result; } + Class* common_elem = ClassJoin(s_ct, t_ct); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + const ClassLoader* class_loader = s->GetClassLoader(); + std::string descriptor("["); + descriptor += ClassHelper(common_elem).GetDescriptor(); + Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader); + DCHECK(array_class != NULL); + return array_class; } else { - // TODO: handle unresolved type cases better? - return cache->Conflict(); - } -} - -bool RegType::CanAccess(const RegType& other) const { - if (Equals(other)) { - return true; // Trivial accessibility. - } else { - bool this_unresolved = IsUnresolvedTypes(); - bool other_unresolved = other.IsUnresolvedTypes(); - if (!this_unresolved && !other_unresolved) { - return GetClass()->CanAccess(other.GetClass()); - } else if (!other_unresolved) { - return other.GetClass()->IsPublic(); // Be conservative, only allow if other is public. + size_t s_depth = s->Depth(); + size_t t_depth = t->Depth(); + // Get s and t to the same depth in the hierarchy + if (s_depth > t_depth) { + while (s_depth > t_depth) { + s = s->GetSuperClass(); + s_depth--; + } } else { - return false; // More complicated test not possible on unresolved types, be conservative. + while (t_depth > s_depth) { + t = t->GetSuperClass(); + t_depth--; + } } - } -} - -bool RegType::CanAccessMember(Class* klass, uint32_t access_flags) const { - if (access_flags & kAccPublic) { - return true; - } - if (!IsUnresolvedTypes()) { - return GetClass()->CanAccessMember(klass, access_flags); - } else { - return false; // More complicated test not possible on unresolved types, be conservative. + // Go up the hierarchy until we get to the common parent + while (s != t) { + s = s->GetSuperClass(); + t = t->GetSuperClass(); + } + return s; } } @@ -157,11 +181,6 @@ bool RegType::IsAssignableFrom(const RegType& src) const { GetClass()->IsAssignableFrom(src.GetClass())) { // We're assignable from the Class point-of-view return true; - } else if (IsUnresolvedTypes() && src.IsUnresolvedTypes() && - GetDescriptor() == src.GetDescriptor()) { - // Two unresolved types (maybe one is uninitialized), we're clearly assignable if the - // descriptor is the same. - return true; } else { return false; } @@ -175,13 +194,13 @@ static const RegType& SelectNonConstant(const RegType& a, const RegType& b) { const RegType& RegType::Merge(const RegType& incoming_type, RegTypeCache* reg_types) const { DCHECK(!Equals(incoming_type)); // Trivial equality handled by caller - if (IsUndefined() && incoming_type.IsUndefined()) { - return *this; // Undefined MERGE Undefined => Undefined + if (IsUnknown() && incoming_type.IsUnknown()) { + return *this; // Unknown MERGE Unknown => Unknown } else if (IsConflict()) { return *this; // Conflict MERGE * => Conflict } else if (incoming_type.IsConflict()) { return incoming_type; // * MERGE Conflict => Conflict - } else if (IsUndefined() || incoming_type.IsUndefined()) { + } else if (IsUnknown() || incoming_type.IsUnknown()) { return reg_types->Conflict(); // Unknown MERGE * => Conflict } else if (IsConstant() && incoming_type.IsConstant()) { int32_t val1 = ConstantValue(); @@ -272,58 +291,6 @@ const RegType& RegType::Merge(const RegType& incoming_type, RegTypeCache* reg_ty } } -// See comment in reg_type.h -Class* RegType::ClassJoin(Class* s, Class* t) { - DCHECK(!s->IsPrimitive()) << PrettyClass(s); - DCHECK(!t->IsPrimitive()) << PrettyClass(t); - if (s == t) { - return s; - } else if (s->IsAssignableFrom(t)) { - return s; - } else if (t->IsAssignableFrom(s)) { - return t; - } else if (s->IsArrayClass() && t->IsArrayClass()) { - Class* s_ct = s->GetComponentType(); - Class* t_ct = t->GetComponentType(); - if (s_ct->IsPrimitive() || t_ct->IsPrimitive()) { - // Given the types aren't the same, if either array is of primitive types then the only - // common parent is java.lang.Object - Class* result = s->GetSuperClass(); // short-cut to java.lang.Object - DCHECK(result->IsObjectClass()); - return result; - } - Class* common_elem = ClassJoin(s_ct, t_ct); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - const ClassLoader* class_loader = s->GetClassLoader(); - std::string descriptor("["); - descriptor += ClassHelper(common_elem).GetDescriptor(); - Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader); - DCHECK(array_class != NULL); - return array_class; - } else { - size_t s_depth = s->Depth(); - size_t t_depth = t->Depth(); - // Get s and t to the same depth in the hierarchy - if (s_depth > t_depth) { - while (s_depth > t_depth) { - s = s->GetSuperClass(); - s_depth--; - } - } else { - while (t_depth > s_depth) { - t = t->GetSuperClass(); - t_depth--; - } - } - // Go up the hierarchy until we get to the common parent - while (s != t) { - s = s->GetSuperClass(); - t = t->GetSuperClass(); - } - return s; - } -} - std::ostream& operator<<(std::ostream& os, const RegType& rhs) { os << rhs.Dump(); return os; diff --git a/src/verifier/reg_type.h b/src/verifier/reg_type.h index 9f89d07fc4..3b4e10b1a4 100644 --- a/src/verifier/reg_type.h +++ b/src/verifier/reg_type.h @@ -33,11 +33,8 @@ class RegTypeCache; class RegType { public: enum Type { - // A special state that identifies a register as undefined. - kRegTypeUndefined = 0, - // The bottom type, used to denote the type of operations such as returning a void, throwing - // an exception or merging incompatible types, such as an int and a long. - kRegTypeConflict, + kRegTypeUnknown = 0, // Initial state. + kRegTypeConflict, // Merge clash makes this reg's type unknowable. kRegTypeBoolean, // Z. kRegType1nrSTART = kRegTypeBoolean, kRegTypeIntegralSTART = kRegTypeBoolean, @@ -60,8 +57,6 @@ class RegType { kRegTypeUninitializedReference, // Freshly allocated reference type. kRegTypeUninitializedThisReference, // Freshly allocated reference passed as "this". kRegTypeUnresolvedAndUninitializedReference, // Freshly allocated unresolved reference type. - // Freshly allocated unresolved reference passed as "this". - kRegTypeUnresolvedAndUninitializedThisReference, kRegTypeReference, // Reference type. }; @@ -69,7 +64,7 @@ class RegType { return type_; } - bool IsUndefined() const { return type_ == kRegTypeUndefined; } + bool IsUnknown() const { return type_ == kRegTypeUnknown; } bool IsConflict() const { return type_ == kRegTypeConflict; } bool IsBoolean() const { return type_ == kRegTypeBoolean; } bool IsByte() const { return type_ == kRegTypeByte; } @@ -85,17 +80,13 @@ class RegType { bool IsUnresolvedAndUninitializedReference() const { return type_ == kRegTypeUnresolvedAndUninitializedReference; } - bool IsUnresolvedAndUninitializedThisReference() const { - return type_ == kRegTypeUnresolvedAndUninitializedThisReference; - } bool IsReference() const { return type_ == kRegTypeReference; } bool IsUninitializedTypes() const { return IsUninitializedReference() || IsUninitializedThisReference() || - IsUnresolvedAndUninitializedReference() || IsUnresolvedAndUninitializedThisReference(); + IsUnresolvedAndUninitializedReference(); } bool IsUnresolvedTypes() const { - return IsUnresolvedReference() || IsUnresolvedAndUninitializedReference() || - IsUnresolvedAndUninitializedThisReference(); + return IsUnresolvedReference() || IsUnresolvedAndUninitializedReference(); } bool IsLowHalf() const { return type_ == kRegTypeLongLo || type_ == kRegTypeDoubleLo || @@ -144,14 +135,12 @@ class RegType { } bool IsReferenceTypes() const { - return IsReference() || IsUnresolvedReference() || IsZero() || - IsUninitializedReference() || IsUninitializedThisReference() || - IsUnresolvedAndUninitializedReference() || IsUnresolvedAndUninitializedThisReference(); + return IsReference() || IsUnresolvedReference() || IsUninitializedReference() || + IsUninitializedThisReference() || IsUnresolvedAndUninitializedReference() || IsZero(); } bool IsNonZeroReferenceTypes() const { - return IsReference() || IsUnresolvedReference() || IsZero() || - IsUninitializedReference() || IsUninitializedThisReference() || - IsUnresolvedAndUninitializedReference() || IsUnresolvedAndUninitializedThisReference(); + return IsReference() || IsUnresolvedReference() || IsUninitializedReference() || + IsUninitializedThisReference(); } bool IsCategory1Types() const { return (type_ >= kRegType1nrSTART && type_ <= kRegType1nrEND) || IsConstant(); @@ -270,23 +259,14 @@ class RegType { return cache_id_; } - const RegType& GetSuperClass(RegTypeCache* cache) const; - std::string Dump() const; - // Can this type access other? - bool CanAccess(const RegType& other) const; - // Can this type access a member with the given properties? - bool CanAccessMember(Class* klass, uint32_t access_flags) const; - - // Can this type be assigned by src? bool IsAssignableFrom(const RegType& src) const; - bool Equals(const RegType& other) const { return GetId() == other.GetId(); } - - // Compute the merge of this register from one edge (path) with incoming_type from another. const RegType& Merge(const RegType& incoming_type, RegTypeCache* reg_types) const; + bool Equals(const RegType& other) const { return GetId() == other.GetId(); } + /* * A basic Join operation on classes. For a pair of types S and T the Join, written S v T = J, is * S <: J, T <: J and for-all U such that S <: U, T <: U then J <: U. That is J is the parent of @@ -312,7 +292,7 @@ class RegType { type_(type), klass_or_descriptor_(klass_or_descriptor), allocation_pc_or_constant_(allocation_pc_or_constant), cache_id_(cache_id) { DCHECK(IsConstant() || IsUninitializedTypes() || allocation_pc_or_constant == 0); - if (!IsConstant() && !IsLongConstant() && !IsLongConstantHigh() && !IsUndefined() && + if (!IsConstant() && !IsLongConstant() && !IsLongConstantHigh() && !IsUnknown() && !IsConflict()) { DCHECK(klass_or_descriptor != NULL); DCHECK(IsUnresolvedTypes() || klass_or_descriptor_->IsClass()); diff --git a/src/verifier/reg_type_cache.cc b/src/verifier/reg_type_cache.cc index 53b7a76488..d06377a177 100644 --- a/src/verifier/reg_type_cache.cc +++ b/src/verifier/reg_type_cache.cc @@ -32,7 +32,7 @@ static RegType::Type RegTypeFromPrimitiveType(Primitive::Type prim_type) { case Primitive::kPrimFloat: return RegType::kRegTypeFloat; case Primitive::kPrimDouble: return RegType::kRegTypeDoubleLo; case Primitive::kPrimVoid: - default: return RegType::kRegTypeConflict; + default: return RegType::kRegTypeUnknown; } } @@ -48,12 +48,12 @@ static RegType::Type RegTypeFromDescriptor(const std::string& descriptor) { case 'F': return RegType::kRegTypeFloat; case 'D': return RegType::kRegTypeDoubleLo; case 'V': - default: return RegType::kRegTypeConflict; + default: return RegType::kRegTypeUnknown; } } else if (descriptor[0] == 'L' || descriptor[0] == '[') { return RegType::kRegTypeReference; } else { - return RegType::kRegTypeConflict; + return RegType::kRegTypeUnknown; } } @@ -115,7 +115,7 @@ const RegType& RegTypeCache::From(RegType::Type type, const ClassLoader* loader, } else { // The descriptor is broken return the unknown type as there's nothing sensible that // could be done at runtime - return Conflict(); + return Unknown(); } } } @@ -201,30 +201,15 @@ const RegType& RegTypeCache::FromUninitialized(const RegType& uninit_type) { return *entry; } -const RegType& RegTypeCache::UninitializedThisArgument(const RegType& type) { - // TODO: implement descriptor version. - RegType* entry; - if (type.IsUnresolvedTypes()) { - String* descriptor = type.GetDescriptor(); - for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) { - RegType* cur_entry = entries_[i]; - if (cur_entry->IsUnresolvedAndUninitializedThisReference() && - cur_entry->GetDescriptor() == descriptor) { - return *cur_entry; - } - } - entry = new RegType(RegType::kRegTypeUnresolvedAndUninitializedThisReference, descriptor, 0, - entries_.size()); - } else { - Class* klass = type.GetClass(); - for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) { - RegType* cur_entry = entries_[i]; - if (cur_entry->IsUninitializedThisReference() && cur_entry->GetClass() == klass) { - return *cur_entry; - } +const RegType& RegTypeCache::UninitializedThisArgument(Class* klass) { + for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) { + RegType* cur_entry = entries_[i]; + if (cur_entry->IsUninitializedThisReference() && cur_entry->GetClass() == klass) { + return *cur_entry; } - entry = new RegType(RegType::kRegTypeUninitializedThisReference, klass, 0, entries_.size()); } + RegType* entry = new RegType(RegType::kRegTypeUninitializedThisReference, klass, 0, + entries_.size()); entries_.push_back(entry); return *entry; } diff --git a/src/verifier/reg_type_cache.h b/src/verifier/reg_type_cache.h index 51eccd536b..9c5e227c32 100644 --- a/src/verifier/reg_type_cache.h +++ b/src/verifier/reg_type_cache.h @@ -27,7 +27,7 @@ namespace verifier { class RegTypeCache { public: explicit RegTypeCache() : entries_(RegType::kRegTypeLastFixedLocation + 1) { - Undefined(); // ensure Undefined is initialized + Unknown(); // ensure Unknown is initialized } ~RegTypeCache() { STLDeleteElements(&entries_); @@ -60,14 +60,13 @@ class RegTypeCache { const RegType& JavaLangString() { return From(RegType::kRegTypeReference, NULL, "Ljava/lang/String;"); } const RegType& JavaLangThrowable() { return From(RegType::kRegTypeReference, NULL, "Ljava/lang/Throwable;"); } - const RegType& Undefined(){ return FromType(RegType::kRegTypeUndefined); } + const RegType& Unknown() { return FromType(RegType::kRegTypeUnknown); } const RegType& Conflict() { return FromType(RegType::kRegTypeConflict); } const RegType& ConstLo() { return FromType(RegType::kRegTypeConstLo); } const RegType& Zero() { return FromCat1Const(0); } const RegType& Uninitialized(const RegType& type, uint32_t allocation_pc); - // Create an uninitialized 'this' argument for the given type. - const RegType& UninitializedThisArgument(const RegType& type); + const RegType& UninitializedThisArgument(Class* klass); const RegType& FromUninitialized(const RegType& uninit_type); // Representatives of various constant types. When merging constants we can't infer a type, diff --git a/src/verifier/reg_type_test.cc b/src/verifier/reg_type_test.cc index 18c165597e..52e42285b5 100644 --- a/src/verifier/reg_type_test.cc +++ b/src/verifier/reg_type_test.cc @@ -29,7 +29,7 @@ TEST_F(RegTypeTest, Primitives) { RegTypeCache cache; const RegType& bool_reg_type = cache.Boolean(); - EXPECT_FALSE(bool_reg_type.IsUndefined()); + EXPECT_FALSE(bool_reg_type.IsUnknown()); EXPECT_FALSE(bool_reg_type.IsConflict()); EXPECT_FALSE(bool_reg_type.IsZero()); EXPECT_FALSE(bool_reg_type.IsOne()); @@ -60,7 +60,7 @@ TEST_F(RegTypeTest, Primitives) { EXPECT_TRUE(bool_reg_type.IsArrayIndexTypes()); const RegType& byte_reg_type = cache.Byte(); - EXPECT_FALSE(byte_reg_type.IsUndefined()); + EXPECT_FALSE(byte_reg_type.IsUnknown()); EXPECT_FALSE(byte_reg_type.IsConflict()); EXPECT_FALSE(byte_reg_type.IsZero()); EXPECT_FALSE(byte_reg_type.IsOne()); @@ -91,7 +91,7 @@ TEST_F(RegTypeTest, Primitives) { EXPECT_TRUE(byte_reg_type.IsArrayIndexTypes()); const RegType& char_reg_type = cache.Char(); - EXPECT_FALSE(char_reg_type.IsUndefined()); + EXPECT_FALSE(char_reg_type.IsUnknown()); EXPECT_FALSE(char_reg_type.IsConflict()); EXPECT_FALSE(char_reg_type.IsZero()); EXPECT_FALSE(char_reg_type.IsOne()); @@ -122,7 +122,7 @@ TEST_F(RegTypeTest, Primitives) { EXPECT_TRUE(char_reg_type.IsArrayIndexTypes()); const RegType& short_reg_type = cache.Short(); - EXPECT_FALSE(short_reg_type.IsUndefined()); + EXPECT_FALSE(short_reg_type.IsUnknown()); EXPECT_FALSE(short_reg_type.IsConflict()); EXPECT_FALSE(short_reg_type.IsZero()); EXPECT_FALSE(short_reg_type.IsOne()); @@ -153,7 +153,7 @@ TEST_F(RegTypeTest, Primitives) { EXPECT_TRUE(short_reg_type.IsArrayIndexTypes()); const RegType& int_reg_type = cache.Integer(); - EXPECT_FALSE(int_reg_type.IsUndefined()); + EXPECT_FALSE(int_reg_type.IsUnknown()); EXPECT_FALSE(int_reg_type.IsConflict()); EXPECT_FALSE(int_reg_type.IsZero()); EXPECT_FALSE(int_reg_type.IsOne()); @@ -184,7 +184,7 @@ TEST_F(RegTypeTest, Primitives) { EXPECT_TRUE(int_reg_type.IsArrayIndexTypes()); const RegType& long_reg_type = cache.Long(); - EXPECT_FALSE(long_reg_type.IsUndefined()); + EXPECT_FALSE(long_reg_type.IsUnknown()); EXPECT_FALSE(long_reg_type.IsConflict()); EXPECT_FALSE(long_reg_type.IsZero()); EXPECT_FALSE(long_reg_type.IsOne()); @@ -215,7 +215,7 @@ TEST_F(RegTypeTest, Primitives) { EXPECT_FALSE(long_reg_type.IsArrayIndexTypes()); const RegType& float_reg_type = cache.Float(); - EXPECT_FALSE(float_reg_type.IsUndefined()); + EXPECT_FALSE(float_reg_type.IsUnknown()); EXPECT_FALSE(float_reg_type.IsConflict()); EXPECT_FALSE(float_reg_type.IsZero()); EXPECT_FALSE(float_reg_type.IsOne()); @@ -246,7 +246,7 @@ TEST_F(RegTypeTest, Primitives) { EXPECT_FALSE(float_reg_type.IsArrayIndexTypes()); const RegType& double_reg_type = cache.Double(); - EXPECT_FALSE(double_reg_type.IsUndefined()); + EXPECT_FALSE(double_reg_type.IsUnknown()); EXPECT_FALSE(double_reg_type.IsConflict()); EXPECT_FALSE(double_reg_type.IsZero()); EXPECT_FALSE(double_reg_type.IsOne()); diff --git a/src/verifier/register_line.cc b/src/verifier/register_line.cc index 085a101a6c..6a86411309 100644 --- a/src/verifier/register_line.cc +++ b/src/verifier/register_line.cc @@ -23,8 +23,7 @@ namespace verifier { bool RegisterLine::CheckConstructorReturn() const { for (size_t i = 0; i < num_regs_; i++) { - if (GetRegisterType(i).IsUninitializedThisReference() || - GetRegisterType(i).IsUnresolvedAndUninitializedThisReference()) { + if (GetRegisterType(i).IsUninitializedThisReference()) { verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Constructor returning without calling superclass constructor"; return false; @@ -34,7 +33,7 @@ bool RegisterLine::CheckConstructorReturn() const { } bool RegisterLine::SetRegisterType(uint32_t vdst, const RegType& new_type) { - DCHECK_LT(vdst, num_regs_); + DCHECK(vdst < num_regs_); if (new_type.IsLowHalf()) { line_[vdst] = new_type.GetId(); line_[vdst + 1] = new_type.HighHalf(verifier_->GetRegTypeCache()).GetId(); @@ -54,8 +53,9 @@ bool RegisterLine::SetRegisterType(uint32_t vdst, const RegType& new_type) { } void RegisterLine::SetResultTypeToUnknown() { - result_[0] = RegType::kRegTypeUndefined; - result_[1] = RegType::kRegTypeUndefined; + uint16_t unknown_id = verifier_->GetRegTypeCache()->Unknown().GetId(); + result_[0] = unknown_id; + result_[1] = unknown_id; } void RegisterLine::SetResultRegisterType(const RegType& new_type) { @@ -64,7 +64,7 @@ void RegisterLine::SetResultRegisterType(const RegType& new_type) { DCHECK_EQ(new_type.HighHalf(verifier_->GetRegTypeCache()).GetId(), new_type.GetId() + 1); result_[1] = new_type.GetId() + 1; } else { - result_[1] = RegType::kRegTypeUndefined; + result_[1] = verifier_->GetRegTypeCache()->Unknown().GetId(); } } @@ -77,14 +77,14 @@ const RegType& RegisterLine::GetRegisterType(uint32_t vsrc) const { const RegType& RegisterLine::GetInvocationThis(const DecodedInstruction& dec_insn) { if (dec_insn.vA < 1) { verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invoke lacks 'this'"; - return verifier_->GetRegTypeCache()->Conflict(); + return verifier_->GetRegTypeCache()->Unknown(); } /* get the element type of the array held in vsrc */ const RegType& this_type = GetRegisterType(dec_insn.vC); if (!this_type.IsReferenceTypes()) { verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "tried to get class from non-reference register v" << dec_insn.vC << " (type=" << this_type << ")"; - return verifier_->GetRegTypeCache()->Conflict(); + return verifier_->GetRegTypeCache()->Unknown(); } return this_type; } @@ -168,9 +168,9 @@ void RegisterLine::CopyResultRegister1(uint32_t vdst, bool is_reference) { verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "copyRes1 v" << vdst << "<- result0" << " type=" << type; } else { - DCHECK(verifier_->GetRegTypeCache()->GetFromId(result_[1]).IsUndefined()); + DCHECK(verifier_->GetRegTypeCache()->GetFromId(result_[1]).IsUnknown()); SetRegisterType(vdst, type); - result_[0] = RegType::kRegTypeUndefined; + result_[0] = verifier_->GetRegTypeCache()->Unknown().GetId(); } } @@ -187,8 +187,8 @@ void RegisterLine::CopyResultRegister2(uint32_t vdst) { } else { DCHECK(type_l.CheckWidePair(type_h)); // Set should never allow this case SetRegisterType(vdst, type_l); // also sets the high - result_[0] = RegType::kRegTypeUndefined; - result_[1] = RegType::kRegTypeUndefined; + result_[0] = verifier_->GetRegTypeCache()->Unknown().GetId(); + result_[1] = verifier_->GetRegTypeCache()->Unknown().GetId(); } } diff --git a/src/verifier/register_line.h b/src/verifier/register_line.h index f214b8113a..6b921cc237 100644 --- a/src/verifier/register_line.h +++ b/src/verifier/register_line.h @@ -56,8 +56,8 @@ class RegisterLine { RegisterLine(size_t num_regs, MethodVerifier* verifier) : line_(new uint16_t[num_regs]), verifier_(verifier), num_regs_(num_regs) { memset(line_.get(), 0, num_regs_ * sizeof(uint16_t)); - result_[0] = RegType::kRegTypeUndefined; - result_[1] = RegType::kRegTypeUndefined; + result_[0] = RegType::kRegTypeUnknown; + result_[1] = RegType::kRegTypeUnknown; } // Implement category-1 "move" instructions. Copy a 32-bit value from "vsrc" to "vdst". @@ -285,7 +285,7 @@ class RegisterLine { MethodVerifier* verifier_; // Length of reg_types_ - const uint32_t num_regs_; + const size_t num_regs_; // A stack of monitor enter locations std::deque<uint32_t> monitors_; // A map from register to a bit vector of indices into the monitors_ stack. As we pop the monitor |