Access checks may fail verification due to class loaders
At compile time we have incomplete class loader information. We need to
be tolerant and make this a runtime issue.
Change-Id: I9e63b804ae68434753f0aea7a855014030a1542d
diff --git a/src/compiler.h b/src/compiler.h
index a8c596a..b43d5f6 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -94,16 +94,17 @@
bool CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexCache* dex_cache,
const DexFile& dex_file, uint32_t type_idx) const {
Class* resolved_class = dex_cache->GetResolvedType(type_idx);
- // We should never ask whether a type needs access checks to raise a verification error,
- // all other cases where this following test could fail should have been rewritten by the
- // verifier to verification errors. Also need to handle a lack of knowledge at compile time.
-#ifndef NDEBUG
+ if (resolved_class == NULL) {
+ return false; // Unknown class needs access checks.
+ }
const DexFile::MethodId& method_id = dex_file.GetMethodId(referrer_idx);
Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_);
- DCHECK(resolved_class == NULL || referrer_class == NULL ||
- referrer_class->CanAccess(resolved_class));
-#endif
- return resolved_class != NULL;
+ if (referrer_class == NULL) {
+ return false; // Incomplete referrer knowledge needs access check.
+ }
+ // Perform access check, will return true if access is ok or false if we're going to have to
+ // check this at runtime (for example for class loaders).
+ return referrer_class->CanAccess(resolved_class);
}
private:
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
index adf0288..2fb6492 100644
--- a/src/dex_verifier.cc
+++ b/src/dex_verifier.cc
@@ -1032,13 +1032,18 @@
std::ostream& DexVerifier::Fail(VerifyError error) {
CHECK_EQ(failure_, VERIFY_ERROR_NONE);
- // If we're optimistically running verification at compile time, turn NO_xxx errors into generic
- // errors so that we reverify at runtime
+ // If we're optimistically running verification at compile time, turn NO_xxx and ACCESS_xxx
+ // errors into generic errors so that we re-verify at runtime. We may fail to find or to agree
+ // on access because of not yet available class loaders, or class loaders that will differ at
+ // runtime.
if (Runtime::Current()->IsCompiler()) {
switch(error) {
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_GENERIC;
break;
default:
@@ -2156,30 +2161,38 @@
}
case Instruction::NEW_ARRAY: {
const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vC_);
- // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
- if (!res_type.IsArrayTypes()) {
- Fail(VERIFY_ERROR_GENERIC) << "new-array on non-array class " << res_type;
+ if (res_type.IsUnknown()) {
+ CHECK_NE(failure_, VERIFY_ERROR_NONE);
} 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_, res_type);
+ // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
+ if (!res_type.IsArrayTypes()) {
+ 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_, res_type);
+ }
}
break;
}
case Instruction::FILLED_NEW_ARRAY:
case Instruction::FILLED_NEW_ARRAY_RANGE: {
const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vB_);
- // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
- if (!res_type.IsArrayTypes()) {
- Fail(VERIFY_ERROR_GENERIC) << "filled-new-array on non-array class";
+ if (res_type.IsUnknown()) {
+ CHECK_NE(failure_, VERIFY_ERROR_NONE);
} else {
- bool is_range = (dec_insn.opcode_ == Instruction::FILLED_NEW_ARRAY_RANGE);
- /* check the arguments to the instruction */
- VerifyFilledNewArrayRegs(dec_insn, res_type, is_range);
- /* filled-array result goes into "result" register */
- work_line_->SetResultRegisterType(res_type);
- just_set_result = true;
+ // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
+ if (!res_type.IsArrayTypes()) {
+ 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_type, is_range);
+ /* filled-array result goes into "result" register */
+ work_line_->SetResultRegisterType(res_type);
+ just_set_result = true;
+ }
}
break;
}
diff --git a/src/dex_verifier.h b/src/dex_verifier.h
index cbf26d6..d8792cf 100644
--- a/src/dex_verifier.h
+++ b/src/dex_verifier.h
@@ -216,7 +216,7 @@
bool IsArrayTypes() const {
if (IsUnresolvedTypes()) {
return GetDescriptor()->CharAt(0) == '[';
- } else if (!IsConstant()) {
+ } else if (IsReference()) {
return GetClass()->IsArrayClass();
} else {
return false;
@@ -228,7 +228,7 @@
// Primitive arrays will always resolve
DCHECK(GetDescriptor()->CharAt(1) == 'L' || GetDescriptor()->CharAt(1) == '[');
return GetDescriptor()->CharAt(0) == '[';
- } else if (!IsConstant()) {
+ } else if (IsReference()) {
Class* type = GetClass();
return type->IsArrayClass() && !type->GetComponentType()->IsPrimitive();
} else {