Support for unresolved types in new-instance during verification.
Also, ensure that classes that don't load are erroneous, warn early
about exceptions left on a thread by the verifier/compiler, factor out
slowpath checks for the compiler and fix the slowpath selector for
const-class.
This change causes more dex cache misses at runtime (more slowpath
execution). It also requires a "mm clean-oat".
Change-Id: I014b49ebdd7d8f7dd2e39cc0958fc0b708d58c4c
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
index 79a455e..79f90e7 100644
--- a/src/dex_verifier.cc
+++ b/src/dex_verifier.cc
@@ -43,7 +43,7 @@
"Unresolved Reference",
"Uninitialized Reference",
"Uninitialized This Reference",
- "Unresolved And Uninitialized This Reference",
+ "Unresolved And Uninitialized Reference",
"Reference",
};
@@ -65,7 +65,7 @@
result = type_strings[type_];
if (IsReferenceTypes()) {
result += ": ";
- if (IsUnresolvedReference()) {
+ if (IsUnresolvedTypes()) {
result += PrettyDescriptor(GetDescriptor());
} else {
result += PrettyDescriptor(GetClass()->GetDescriptor());
@@ -370,15 +370,23 @@
entries_.push_back(entry);
return *entry;
} else {
+ // TODO: we assume unresolved, but we may be able to do better by validating whether the
+ // descriptor string is valid
// Unable to resolve so create unresolved register type
DCHECK(Thread::Current()->IsExceptionPending());
Thread::Current()->ClearException();
- String* string_descriptor =
- Runtime::Current()->GetInternTable()->InternStrong(descriptor.c_str());
- RegType* entry = new RegType(RegType::kRegTypeUnresolvedReference, string_descriptor, 0,
- entries_.size());
- entries_.push_back(entry);
- return *entry;
+ if (IsValidDescriptor(descriptor.c_str())) {
+ String* string_descriptor =
+ Runtime::Current()->GetInternTable()->InternStrong(descriptor.c_str());
+ RegType* entry = new RegType(RegType::kRegTypeUnresolvedReference, string_descriptor, 0,
+ entries_.size());
+ entries_.push_back(entry);
+ return *entry;
+ } else {
+ // The descriptor is broken return the unknown type as there's nothing sensible that
+ // could be done at runtime
+ return Unknown();
+ }
}
}
}
@@ -407,15 +415,58 @@
}
}
-const RegType& RegTypeCache::Uninitialized(Class* klass, uint32_t allocation_pc) {
- for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
- RegType* cur_entry = entries_[i];
- if (cur_entry->IsUninitializedReference() && cur_entry->GetAllocationPc() == allocation_pc &&
- cur_entry->GetClass() == klass) {
- return *cur_entry;
+const RegType& RegTypeCache::Uninitialized(const RegType& type, uint32_t allocation_pc) {
+ 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->IsUnresolvedAndUninitializedReference() &&
+ cur_entry->GetAllocationPc() == allocation_pc &&
+ cur_entry->GetDescriptor() == descriptor) {
+ return *cur_entry;
+ }
}
+ entry = new RegType(RegType::kRegTypeUnresolvedAndUninitializedReference,
+ descriptor, allocation_pc, 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->IsUninitializedReference() &&
+ cur_entry->GetAllocationPc() == allocation_pc &&
+ cur_entry->GetClass() == klass) {
+ return *cur_entry;
+ }
+ }
+ entry = new RegType(RegType::kRegTypeUninitializedReference,
+ klass, allocation_pc, entries_.size());
}
- RegType* entry = new RegType(RegType::kRegTypeUninitializedReference, klass, allocation_pc, entries_.size());
+ entries_.push_back(entry);
+ return *entry;
+}
+
+const RegType& RegTypeCache::FromUninitialized(const RegType& uninit_type) {
+ RegType* entry;
+ if (uninit_type.IsUnresolvedTypes()) {
+ String* descriptor = uninit_type.GetDescriptor();
+ for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
+ RegType* cur_entry = entries_[i];
+ if (cur_entry->IsUnresolvedReference() && cur_entry->GetDescriptor() == descriptor) {
+ return *cur_entry;
+ }
+ }
+ entry = new RegType(RegType::kRegTypeUnresolvedReference, descriptor, 0, entries_.size());
+ } else {
+ Class* klass = uninit_type.GetClass();
+ for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
+ RegType* cur_entry = entries_[i];
+ if (cur_entry->IsReference() && cur_entry->GetClass() == klass) {
+ return *cur_entry;
+ }
+ }
+ entry = new RegType(RegType::kRegTypeReference, klass, 0, entries_.size());
+ }
entries_.push_back(entry);
return *entry;
}
@@ -462,6 +513,18 @@
return *entry;
}
+const RegType& RegTypeCache::GetComponentType(const RegType& array, const ClassLoader* loader) {
+ CHECK(array.IsArrayClass());
+ if (array.IsUnresolvedTypes()) {
+ std::string descriptor = array.GetDescriptor()->ToModifiedUtf8();
+ std::string component = descriptor.substr(1, descriptor.size() - 1);
+ return FromDescriptor(loader, component);
+ } else {
+ return FromClass(array.GetClass()->GetComponentType());
+ }
+}
+
+
bool RegisterLine::CheckConstructorReturn() const {
for (size_t i = 0; i < num_regs_; i++) {
if (GetRegisterType(i).IsUninitializedThisReference()) {
@@ -561,20 +624,16 @@
}
void RegisterLine::MarkRefsAsInitialized(const RegType& uninit_type) {
- Class* klass = uninit_type.GetClass();
- if (klass == NULL) {
- verifier_->Fail(VERIFY_ERROR_GENERIC) << "Unable to find type=" << uninit_type;
- } else {
- const RegType& init_type = verifier_->GetRegTypeCache()->FromClass(klass);
- size_t changed = 0;
- for (size_t i = 0; i < num_regs_; i++) {
- if (GetRegisterType(i).Equals(uninit_type)) {
- line_[i] = init_type.GetId();
- changed++;
- }
+ DCHECK(uninit_type.IsUninitializedTypes());
+ const RegType& init_type = verifier_->GetRegTypeCache()->FromUninitialized(uninit_type);
+ size_t changed = 0;
+ for (size_t i = 0; i < num_regs_; i++) {
+ if (GetRegisterType(i).Equals(uninit_type)) {
+ line_[i] = init_type.GetId();
+ changed++;
}
- DCHECK_GT(changed, 0u);
}
+ DCHECK_GT(changed, 0u);
}
void RegisterLine::MarkUninitRefsAsInvalid(const RegType& uninit_type) {
@@ -895,8 +954,9 @@
<< verifier.info_messages_.str() << Dumpable<DexVerifier>(verifier);
}
-DexVerifier::DexVerifier(Method* method) : java_lang_throwable_(NULL), work_insn_idx_(-1),
- method_(method), failure_(VERIFY_ERROR_NONE),
+DexVerifier::DexVerifier(Method* method) : work_insn_idx_(-1), method_(method),
+ failure_(VERIFY_ERROR_NONE),
+
new_instance_count_(0), monitor_enter_count_(0) {
const DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -994,15 +1054,26 @@
/* Iterate over each of the handlers to verify target addresses. */
const byte* handlers_ptr = DexFile::dexGetCatchHandlerData(*code_item_, 0);
uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
+ ClassLinker* linker = Runtime::Current()->GetClassLinker();
for (uint32_t idx = 0; idx < handlers_size; idx++) {
DexFile::CatchHandlerIterator iterator(handlers_ptr);
for (; !iterator.HasNext(); iterator.Next()) {
- uint32_t dex_pc= iterator.Get().address_;
+ const DexFile::CatchHandlerItem& handler = iterator.Get();
+ uint32_t dex_pc= handler.address_;
if (!insn_flags_[dex_pc].IsOpcode()) {
Fail(VERIFY_ERROR_GENERIC) << "exception handler starts at bad address (" << dex_pc << ")";
return false;
}
insn_flags_[dex_pc].SetBranchTarget();
+ // Ensure exception types are resolved so that they don't need resolution to be delivered,
+ // unresolved exception types will be ignored by exception delivery
+ if (handler.type_idx_ != DexFile::kDexNoIndex) {
+ Class* exception_type = linker->ResolveType(handler.type_idx_, method_);
+ if (exception_type == NULL) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ Thread::Current()->ClearException();
+ }
+ }
}
handlers_ptr = iterator.GetData();
}
@@ -1814,12 +1885,8 @@
* all exception handlers need to have one of these). We verify that as part of extracting the
* exception type from the catch block list.
*/
- Class* res_class = GetCaughtExceptionType();
- if (res_class == NULL) {
- DCHECK(failure_ != VERIFY_ERROR_NONE);
- } else {
- work_line_->SetRegisterType(dec_insn.vA_, reg_types_.FromClass(res_class));
- }
+ const RegType& res_type = GetCaughtExceptionType();
+ work_line_->SetRegisterType(dec_insn.vA_, res_type);
break;
}
case Instruction::RETURN_VOID:
@@ -1911,17 +1978,12 @@
work_line_->SetRegisterType(dec_insn.vA_, reg_types_.JavaLangString());
break;
case Instruction::CONST_CLASS: {
- /* make sure we can resolve the class; access check is important */
- Class* res_class = ResolveClassAndCheckAccess(dec_insn.vB_);
- if (res_class == NULL) {
- const char* bad_class_desc = dex_file_->dexStringByTypeIdx(dec_insn.vB_);
- fail_messages_ << "unable to resolve const-class " << dec_insn.vB_
- << " (" << bad_class_desc << ") in "
- << PrettyDescriptor(method_->GetDeclaringClass()->GetDescriptor());
- DCHECK(failure_ != VERIFY_ERROR_GENERIC);
- } else {
- work_line_->SetRegisterType(dec_insn.vA_, reg_types_.JavaLangClass());
- }
+ // 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, but on error we keep it Unknown
+ work_line_->SetRegisterType(dec_insn.vA_,
+ res_type.IsUnknown() ? res_type : reg_types_.JavaLangClass());
break;
}
case Instruction::MONITOR_ENTER:
@@ -1952,58 +2014,39 @@
work_line_->PopMonitor(dec_insn.vA_);
break;
- case Instruction::CHECK_CAST: {
- /*
- * If this instruction succeeds, we will promote register vA to
- * the type in vB. (This could be a demotion -- not expected, so
- * we don't try to address it.)
- *
- * If it fails, an exception is thrown, which we deal with later
- * by ignoring the update to dec_insn.vA_ when branching to a handler.
- */
- Class* res_class = ResolveClassAndCheckAccess(dec_insn.vB_);
- if (res_class == NULL) {
- const char* bad_class_desc = dex_file_->dexStringByTypeIdx(dec_insn.vB_);
- fail_messages_ << "unable to resolve check-cast " << dec_insn.vB_
- << " (" << bad_class_desc << ") in "
- << PrettyDescriptor(method_->GetDeclaringClass()->GetDescriptor());
- DCHECK(failure_ != VERIFY_ERROR_GENERIC);
- } else {
- const RegType& orig_type = work_line_->GetRegisterType(dec_insn.vA_);
- if (!orig_type.IsReferenceTypes()) {
- Fail(VERIFY_ERROR_GENERIC) << "check-cast on non-reference in v" << dec_insn.vA_;
- } else {
- work_line_->SetRegisterType(dec_insn.vA_, reg_types_.FromClass(res_class));
- }
- }
- break;
- }
+ case Instruction::CHECK_CAST:
case Instruction::INSTANCE_OF: {
- /* make sure we're checking a reference type */
- const RegType& tmp_type = work_line_->GetRegisterType(dec_insn.vB_);
- if (!tmp_type.IsReferenceTypes()) {
- Fail(VERIFY_ERROR_GENERIC) << "vB not a reference (" << tmp_type << ")";
+ /*
+ * If this instruction succeeds, we will "downcast" register vA to the type in vB. (This
+ * could be a "upcast" -- not expected, so we don't try to address it.)
+ *
+ * If it fails, an exception is thrown, which we deal with later by ignoring the update to
+ * dec_insn.vA_ when branching to a handler.
+ */
+ bool is_checkcast = dec_insn.opcode_ == Instruction::CHECK_CAST;
+ const RegType& res_type =
+ ResolveClassAndCheckAccess(is_checkcast ? dec_insn.vB_ : dec_insn.vC_);
+ // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
+ const RegType& orig_type =
+ work_line_->GetRegisterType(is_checkcast ? dec_insn.vA_ : dec_insn.vB_);
+ if (!res_type.IsNonZeroReferenceTypes()) {
+ Fail(VERIFY_ERROR_GENERIC) << "check-cast on unexpected class " << res_type;
+ } else if (!orig_type.IsReferenceTypes()) {
+ Fail(VERIFY_ERROR_GENERIC) << "check-cast on non-reference in v" << dec_insn.vA_;
} else {
- /* make sure we can resolve the class; access check is important */
- Class* res_class = ResolveClassAndCheckAccess(dec_insn.vC_);
- if (res_class == NULL) {
- const char* bad_class_desc = dex_file_->dexStringByTypeIdx(dec_insn.vC_);
- fail_messages_ << "unable to resolve instance of " << dec_insn.vC_
- << " (" << bad_class_desc << ") in "
- << PrettyDescriptor(method_->GetDeclaringClass()->GetDescriptor());
- DCHECK(failure_ != VERIFY_ERROR_GENERIC);
+ if (is_checkcast) {
+ work_line_->SetRegisterType(dec_insn.vA_, res_type);
} else {
- /* result is boolean */
work_line_->SetRegisterType(dec_insn.vA_, reg_types_.Boolean());
}
}
break;
}
case Instruction::ARRAY_LENGTH: {
- Class* res_class = work_line_->GetClassFromRegister(dec_insn.vB_);
- if (failure_ == VERIFY_ERROR_NONE) {
- if (res_class != NULL && !res_class->IsArrayClass()) {
- Fail(VERIFY_ERROR_GENERIC) << "array-length on non-array";
+ const RegType& res_type = work_line_->GetRegisterType(dec_insn.vB_);
+ if (res_type.IsReferenceTypes()) {
+ if (!res_type.IsArrayClass()) {
+ Fail(VERIFY_ERROR_GENERIC) << "array-length on non-array " << res_type;
} else {
work_line_->SetRegisterType(dec_insn.vA_, reg_types_.Integer());
}
@@ -2011,66 +2054,47 @@
break;
}
case Instruction::NEW_INSTANCE: {
- Class* res_class = ResolveClassAndCheckAccess(dec_insn.vB_);
- if (res_class == NULL) {
- const char* bad_class_desc = dex_file_->dexStringByTypeIdx(dec_insn.vB_);
- fail_messages_ << "unable to resolve new-instance " << dec_insn.vB_
- << " (" << bad_class_desc << ") in "
- << PrettyDescriptor(method_->GetDeclaringClass()->GetDescriptor());
- DCHECK(failure_ != VERIFY_ERROR_GENERIC);
+ const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vB_);
+ // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
+ // can't create an instance of an interface or abstract class */
+ if (!res_type.IsInstantiableTypes()) {
+ Fail(VERIFY_ERROR_INSTANTIATION)
+ << "new-instance on primitive, interface or abstract class" << res_type;
} else {
- /* can't create an instance of an interface or abstract class */
- if (res_class->IsPrimitive() || res_class->IsAbstract() || res_class->IsInterface()) {
- Fail(VERIFY_ERROR_INSTANTIATION)
- << "new-instance on primitive, interface or abstract class"
- << PrettyDescriptor(res_class->GetDescriptor());
- } else {
- const RegType& uninit_type = reg_types_.Uninitialized(res_class, work_insn_idx_);
- // Any registers holding previous allocations from this address that have not yet been
- // initialized must be marked invalid.
- work_line_->MarkUninitRefsAsInvalid(uninit_type);
-
- /* add the new uninitialized reference to the register state */
- work_line_->SetRegisterType(dec_insn.vA_, uninit_type);
- }
+ const RegType& uninit_type = reg_types_.Uninitialized(res_type, work_insn_idx_);
+ // Any registers holding previous allocations from this address that have not yet been
+ // initialized must be marked invalid.
+ work_line_->MarkUninitRefsAsInvalid(uninit_type);
+ // add the new uninitialized reference to the register state
+ work_line_->SetRegisterType(dec_insn.vA_, uninit_type);
}
break;
}
case Instruction::NEW_ARRAY: {
- Class* res_class = ResolveClassAndCheckAccess(dec_insn.vC_);
- if (res_class == NULL) {
- const char* bad_class_desc = dex_file_->dexStringByTypeIdx(dec_insn.vC_);
- fail_messages_ << "unable to resolve new-array " << dec_insn.vC_
- << " (" << bad_class_desc << ") in "
- << PrettyDescriptor(method_->GetDeclaringClass()->GetDescriptor());
- DCHECK(failure_ != VERIFY_ERROR_GENERIC);
- } else if (!res_class->IsArrayClass()) {
- Fail(VERIFY_ERROR_GENERIC) << "new-array on non-array class";
+ const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vC_);
+ // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
+ if (!res_type.IsArrayClass()) {
+ Fail(VERIFY_ERROR_GENERIC) << "new-array on non-array class " << res_type;
} else {
/* make sure "size" register is valid type */
work_line_->VerifyRegisterType(dec_insn.vB_, reg_types_.Integer());
/* set register type to array class */
- work_line_->SetRegisterType(dec_insn.vA_, reg_types_.FromClass(res_class));
+ work_line_->SetRegisterType(dec_insn.vA_, res_type);
}
break;
}
case Instruction::FILLED_NEW_ARRAY:
case Instruction::FILLED_NEW_ARRAY_RANGE: {
- Class* res_class = ResolveClassAndCheckAccess(dec_insn.vB_);
- if (res_class == NULL) {
- const char* bad_class_desc = dex_file_->dexStringByTypeIdx(dec_insn.vB_);
- fail_messages_ << "unable to resolve filled-array " << dec_insn.vB_
- << " (" << bad_class_desc << ") in "
- << PrettyDescriptor(method_->GetDeclaringClass()->GetDescriptor());
- DCHECK(failure_ != VERIFY_ERROR_GENERIC);
- } else if (!res_class->IsArrayClass()) {
+ const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vB_);
+ // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
+ if (!res_type.IsArrayClass()) {
Fail(VERIFY_ERROR_GENERIC) << "filled-new-array on non-array class";
} else {
bool is_range = (dec_insn.opcode_ == Instruction::FILLED_NEW_ARRAY_RANGE);
/* check the arguments to the instruction */
- VerifyFilledNewArrayRegs(dec_insn, res_class, is_range);
+ VerifyFilledNewArrayRegs(dec_insn, res_type, is_range);
/* filled-array result goes into "result" register */
- work_line_->SetResultRegisterType(reg_types_.FromClass(res_class));
+ work_line_->SetResultRegisterType(res_type);
just_set_result = true;
}
break;
@@ -2093,12 +2117,9 @@
work_line_->SetRegisterType(dec_insn.vA_, reg_types_.Integer());
break;
case Instruction::THROW: {
- Class* res_class = work_line_->GetClassFromRegister(dec_insn.vA_);
- if (failure_ == VERIFY_ERROR_NONE && res_class != NULL) {
- if (!JavaLangThrowable()->IsAssignableFrom(res_class)) {
- Fail(VERIFY_ERROR_GENERIC) << "thrown class "
- << PrettyDescriptor(res_class->GetDescriptor()) << " not instanceof Throwable";
- }
+ const RegType& res_type = work_line_->GetRegisterType(dec_insn.vA_);
+ if (!reg_types_.JavaLangThrowable().IsAssignableFrom(res_type)) {
+ Fail(VERIFY_ERROR_GENERIC) << "thrown class " << res_type << " not instanceof Throwable";
}
break;
}
@@ -2338,9 +2359,17 @@
dec_insn.opcode_ == Instruction::INVOKE_SUPER_RANGE);
Method* called_method = VerifyInvocationArgs(dec_insn, METHOD_VIRTUAL, is_range, is_super);
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_->dexStringByTypeIdx(return_type_idx);
+ } else {
+ descriptor = called_method->GetReturnTypeDescriptor();
+ }
const RegType& return_type =
- reg_types_.FromDescriptor(called_method->GetDeclaringClass()->GetClassLoader(),
- called_method->GetReturnTypeDescriptor());
+ reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor);
work_line_->SetResultRegisterType(return_type);
just_set_result = true;
}
@@ -2358,7 +2387,16 @@
* 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 (failure_ != VERIFY_ERROR_NONE)
break;
@@ -2368,19 +2406,20 @@
Fail(VERIFY_ERROR_GENERIC) << "unable to initialize null ref";
break;
}
- Class* this_class = this_type.GetClass();
- DCHECK(this_class != NULL);
-
- /* must be in same class or in superclass */
- if (called_method->GetDeclaringClass() == this_class->GetSuperClass()) {
- if (this_class != method_->GetDeclaringClass()) {
- Fail(VERIFY_ERROR_GENERIC)
- << "invoke-direct <init> on super only allowed for 'this' in <init>";
+ if (called_method != NULL) {
+ Class* this_class = this_type.GetClass();
+ DCHECK(this_class != NULL);
+ /* must be in same class or in superclass */
+ if (called_method->GetDeclaringClass() == this_class->GetSuperClass()) {
+ if (this_class != method_->GetDeclaringClass()) {
+ Fail(VERIFY_ERROR_GENERIC)
+ << "invoke-direct <init> on super only allowed for 'this' in <init>";
+ break;
+ }
+ } else if (called_method->GetDeclaringClass() != this_class) {
+ Fail(VERIFY_ERROR_GENERIC) << "invoke-direct <init> must be on current class or super";
break;
}
- } else if (called_method->GetDeclaringClass() != this_class) {
- Fail(VERIFY_ERROR_GENERIC) << "invoke-direct <init> must be on current class or super";
- break;
}
/* arg must be an uninitialized reference */
@@ -2398,9 +2437,17 @@
if (failure_ != VERIFY_ERROR_NONE)
break;
}
+ 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_->dexStringByTypeIdx(return_type_idx);
+ } else {
+ descriptor = called_method->GetReturnTypeDescriptor();
+ }
const RegType& return_type =
- reg_types_.FromDescriptor(called_method->GetDeclaringClass()->GetClassLoader(),
- called_method->GetReturnTypeDescriptor());
+ reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor);
work_line_->SetResultRegisterType(return_type);
just_set_result = true;
}
@@ -2411,9 +2458,17 @@
bool is_range = (dec_insn.opcode_ == Instruction::INVOKE_STATIC_RANGE);
Method* called_method = VerifyInvocationArgs(dec_insn, METHOD_STATIC, is_range, false);
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_->dexStringByTypeIdx(return_type_idx);
+ } else {
+ descriptor = called_method->GetReturnTypeDescriptor();
+ }
const RegType& return_type =
- reg_types_.FromDescriptor(called_method->GetDeclaringClass()->GetClassLoader(),
- called_method->GetReturnTypeDescriptor());
+ reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor);
work_line_->SetResultRegisterType(return_type);
just_set_result = true;
}
@@ -2424,42 +2479,52 @@
bool is_range = (dec_insn.opcode_ == Instruction::INVOKE_INTERFACE_RANGE);
Method* abs_method = VerifyInvocationArgs(dec_insn, METHOD_INTERFACE, is_range, false);
if (failure_ == VERIFY_ERROR_NONE) {
- Class* called_interface = abs_method->GetDeclaringClass();
- if (!called_interface->IsInterface()) {
- Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected interface class in invoke-interface '"
- << PrettyMethod(abs_method) << "'";
- break;
- } else {
- /* 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_GENERIC) << "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.
+ if (abs_method != NULL) {
+ Class* called_interface = abs_method->GetDeclaringClass();
+ if (!called_interface->IsInterface()) {
+ 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 (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_GENERIC) << "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.
}
}
/*
* 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_->dexStringByTypeIdx(return_type_idx);
+ } else {
+ descriptor = abs_method->GetReturnTypeDescriptor();
+ }
const RegType& return_type =
- reg_types_.FromDescriptor(abs_method->GetDeclaringClass()->GetClassLoader(),
- abs_method->GetReturnTypeDescriptor());
+ reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor);
+ work_line_->SetResultRegisterType(return_type);
work_line_->SetResultRegisterType(return_type);
just_set_result = true;
}
@@ -2887,24 +2952,30 @@
return true;
}
-Class* DexVerifier::ResolveClassAndCheckAccess(uint32_t class_idx) {
- const Class* referrer = method_->GetDeclaringClass();
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- Class* res_class = class_linker->ResolveType(*dex_file_, class_idx, referrer);
-
- if (res_class == NULL) {
- Thread::Current()->ClearException();
- Fail(VERIFY_ERROR_NO_CLASS) << "can't find class with index " << (void*) class_idx;
- } else if (!referrer->CanAccess(res_class)) { /* Check if access is allowed. */
- Fail(VERIFY_ERROR_ACCESS_CLASS) << "illegal class access: "
- << referrer->GetDescriptor()->ToModifiedUtf8() << " -> "
- << res_class->GetDescriptor()->ToModifiedUtf8();
+const RegType& DexVerifier::ResolveClassAndCheckAccess(uint32_t class_idx) {
+ const char* descriptor = dex_file_->dexStringByTypeIdx(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(referrer->GetClassLoader(), descriptor);
+ if (klass == NULL && !result.IsUnresolvedTypes()) {
+ method_->GetDexCacheResolvedTypes()->Set(class_idx, result.GetClass());
}
- return res_class;
+ // Check if access is allowed. Unresolved types use AllocObjectFromCodeWithAccessCheck to
+ // check at runtime if access is allowed and so pass here.
+ if (!result.IsUnresolvedTypes() && !referrer->CanAccess(result.GetClass())) {
+ Fail(VERIFY_ERROR_ACCESS_CLASS) << "illegal class access: '"
+ << PrettyDescriptor(referrer->GetDescriptor()) << "' -> '"
+ << result << "'";
+ return reg_types_.Unknown();
+ } else {
+ return result;
+ }
}
-Class* DexVerifier::GetCaughtExceptionType() {
- Class* common_super = NULL;
+const RegType& DexVerifier::GetCaughtExceptionType() {
+ const RegType* common_super = NULL;
if (code_item_->tries_size_ != 0) {
const byte* handlers_ptr = DexFile::dexGetCatchHandlerData(*code_item_, 0);
uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
@@ -2914,25 +2985,23 @@
DexFile::CatchHandlerItem handler = iterator.Get();
if (handler.address_ == (uint32_t) work_insn_idx_) {
if (handler.type_idx_ == DexFile::kDexNoIndex) {
- common_super = JavaLangThrowable();
+ common_super = ®_types_.JavaLangThrowable();
} else {
- Class* klass = ResolveClassAndCheckAccess(handler.type_idx_);
+ const RegType& exception = ResolveClassAndCheckAccess(handler.type_idx_);
/* TODO: on error do we want to keep going? If we don't fail this we run the risk of
* having a non-Throwable introduced at runtime. However, that won't pass an instanceof
* test, so is essentially harmless.
*/
- if (klass == NULL) {
- Fail(VERIFY_ERROR_GENERIC) << "unable to resolve exception class "
- << handler.type_idx_ << " ("
- << dex_file_->dexStringByTypeIdx(handler.type_idx_) << ")";
- return NULL;
- } else if(!JavaLangThrowable()->IsAssignableFrom(klass)) {
- Fail(VERIFY_ERROR_GENERIC) << "unexpected non-exception class " << PrettyClass(klass);
- return NULL;
+ if(!reg_types_.JavaLangThrowable().IsAssignableFrom(exception)) {
+ Fail(VERIFY_ERROR_GENERIC) << "unexpected non-exception class " << exception;
+ return reg_types_.Unknown();
} else if (common_super == NULL) {
- common_super = klass;
+ common_super = &exception;
+ } else if (common_super->Equals(exception)) {
+ // nothing to do
} else {
- common_super = RegType::ClassJoin(common_super, klass);
+ common_super = &common_super->Merge(exception, ®_types_);
+ CHECK(reg_types_.JavaLangThrowable().IsAssignableFrom(*common_super));
}
}
}
@@ -2944,7 +3013,7 @@
/* no catch blocks, or no catches with classes we can find */
Fail(VERIFY_ERROR_GENERIC) << "unable to find exception handler";
}
- return common_super;
+ return *common_super;
}
Method* DexVerifier::ResolveMethodAndCheckAccess(uint32_t method_idx, bool is_direct) {
@@ -2953,11 +3022,11 @@
Method* res_method = dex_cache->GetResolvedMethod(method_idx);
if (res_method == NULL) {
const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
- Class* klass = ResolveClassAndCheckAccess(method_id.class_idx_);
- if (klass == NULL) {
- DCHECK(failure_ != VERIFY_ERROR_NONE);
- return NULL;
+ const RegType& klass_type = ResolveClassAndCheckAccess(method_id.class_idx_);
+ if(klass_type.IsUnresolvedTypes()) {
+ return NULL; // Can't resolve Class so no more to do here
}
+ Class* klass = klass_type.GetClass();
const char* name = dex_file_->GetMethodName(method_id);
std::string signature(dex_file_->CreateMethodDescriptor(method_id.proto_idx_, NULL));
if (is_direct) {
@@ -2991,14 +3060,7 @@
// we're making.
Method* res_method = ResolveMethodAndCheckAccess(dec_insn.vB_,
(method_type == METHOD_DIRECT || method_type == METHOD_STATIC));
- if (res_method == NULL) {
- const DexFile::MethodId& method_id = dex_file_->GetMethodId(dec_insn.vB_);
- const char* method_name = dex_file_->GetMethodName(method_id);
- std::string method_signature = dex_file_->GetMethodSignature(method_id);
- const char* class_descriptor = dex_file_->GetMethodDeclaringClassDescriptor(method_id);
- DCHECK_NE(failure_, VERIFY_ERROR_NONE);
- fail_messages_ << "unable to resolve method " << dec_insn.vB_ << ": "
- << class_descriptor << "." << method_name << " " << method_signature;
+ if (res_method == NULL) { // error or class is unresolved
return NULL;
}
// Make sure calls to constructors are "direct". There are additional restrictions but we don't
@@ -3410,13 +3472,14 @@
}
void DexVerifier::VerifyFilledNewArrayRegs(const Instruction::DecodedInstruction& dec_insn,
- Class* res_class, bool is_range) {
- DCHECK(res_class->IsArrayClass()) << PrettyClass(res_class); // Checked before calling.
+ const RegType& res_type, bool is_range) {
+ DCHECK(res_type.IsArrayClass()) << res_type; // Checked before calling.
/*
* 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_.FromClass(res_class->GetComponentType());
+ 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;
@@ -3663,15 +3726,6 @@
}
}
-Class* DexVerifier::JavaLangThrowable() {
- if (java_lang_throwable_ == NULL) {
- java_lang_throwable_ =
- Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/Throwable;");
- DCHECK(java_lang_throwable_ != NULL);
- }
- return java_lang_throwable_;
-}
-
const uint8_t* PcToReferenceMap::FindBitMap(uint16_t dex_pc, bool error_if_not_present) const {
size_t num_entries = NumEntries();
// Do linear or binary search?