diff options
| -rw-r--r-- | compiler/verifier_deps_test.cc | 10 | ||||
| -rw-r--r-- | runtime/verifier/method_verifier-inl.h | 2 | ||||
| -rw-r--r-- | runtime/verifier/method_verifier.cc | 71 | ||||
| -rw-r--r-- | runtime/verifier/method_verifier.h | 11 | ||||
| -rw-r--r-- | test/954-invoke-polymorphic-verifier/smali/Unresolved.smali | 2 |
5 files changed, 69 insertions, 27 deletions
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 65389252e2..5c097da16f 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -624,7 +624,7 @@ TEST_F(VerifierDepsTest, ConstClass_Resolved) { } TEST_F(VerifierDepsTest, ConstClass_Unresolved) { - ASSERT_TRUE(VerifyMethod("ConstClass_Unresolved")); + ASSERT_FALSE(VerifyMethod("ConstClass_Unresolved")); ASSERT_TRUE(HasClass("LUnresolvedClass;", false)); } @@ -634,7 +634,7 @@ TEST_F(VerifierDepsTest, CheckCast_Resolved) { } TEST_F(VerifierDepsTest, CheckCast_Unresolved) { - ASSERT_TRUE(VerifyMethod("CheckCast_Unresolved")); + ASSERT_FALSE(VerifyMethod("CheckCast_Unresolved")); ASSERT_TRUE(HasClass("LUnresolvedClass;", false)); } @@ -644,7 +644,7 @@ TEST_F(VerifierDepsTest, InstanceOf_Resolved) { } TEST_F(VerifierDepsTest, InstanceOf_Unresolved) { - ASSERT_TRUE(VerifyMethod("InstanceOf_Unresolved")); + ASSERT_FALSE(VerifyMethod("InstanceOf_Unresolved")); ASSERT_TRUE(HasClass("LUnresolvedClass;", false)); } @@ -654,12 +654,12 @@ TEST_F(VerifierDepsTest, NewInstance_Resolved) { } TEST_F(VerifierDepsTest, NewInstance_Unresolved) { - ASSERT_TRUE(VerifyMethod("NewInstance_Unresolved")); + ASSERT_FALSE(VerifyMethod("NewInstance_Unresolved")); ASSERT_TRUE(HasClass("LUnresolvedClass;", false)); } TEST_F(VerifierDepsTest, NewArray_Unresolved) { - ASSERT_TRUE(VerifyMethod("NewArray_Unresolved")); + ASSERT_FALSE(VerifyMethod("NewArray_Unresolved")); ASSERT_TRUE(HasClass("[LUnresolvedClass;", false)); } diff --git a/runtime/verifier/method_verifier-inl.h b/runtime/verifier/method_verifier-inl.h index 6c832e3492..9bb875c033 100644 --- a/runtime/verifier/method_verifier-inl.h +++ b/runtime/verifier/method_verifier-inl.h @@ -77,7 +77,7 @@ inline bool MethodVerifier::HasFailures() const { inline const RegType& MethodVerifier::ResolveCheckedClass(dex::TypeIndex class_idx) { DCHECK(!HasFailures()); - const RegType& result = ResolveClassAndCheckAccess(class_idx); + const RegType& result = ResolveClass<CheckAccess::kYes>(class_idx); DCHECK(!HasFailures()); return result; } diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 312d8dfc18..9a876077d4 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -1765,7 +1765,9 @@ 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 = ResolveClassAndCheckAccess(iterator.GetTypeIdx()); + // Note: don't check access. No error would be thrown for declaring or passing an + // inaccessible class. Only actual accesses to fields or methods will. + const RegType& reg_type = ResolveClass<CheckAccess::kNo>(iterator.GetTypeIdx()); if (!reg_type.IsNonZeroReferenceTypes()) { DCHECK(HasFailures()); return false; @@ -2322,7 +2324,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::CONST_CLASS: { // 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(dex::TypeIndex(inst->VRegB_21c())); + const RegType& res_type = ResolveClass<CheckAccess::kYes>(dex::TypeIndex(inst->VRegB_21c())); // Register holds class, ie its type is class, on error it will hold Conflict. work_line_->SetRegisterType<LockOp::kClear>( this, inst->VRegA_21c(), res_type.IsConflict() ? res_type @@ -2393,7 +2395,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { */ const bool is_checkcast = (inst->Opcode() == Instruction::CHECK_CAST); const dex::TypeIndex type_idx((is_checkcast) ? inst->VRegB_21c() : inst->VRegC_22c()); - const RegType& res_type = ResolveClassAndCheckAccess(type_idx); + const RegType& res_type = ResolveClass<CheckAccess::kYes>(type_idx); if (res_type.IsConflict()) { // If this is a primitive type, fail HARD. ObjPtr<mirror::Class> klass = @@ -2463,7 +2465,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { break; } case Instruction::NEW_INSTANCE: { - const RegType& res_type = ResolveClassAndCheckAccess(dex::TypeIndex(inst->VRegB_21c())); + const RegType& res_type = ResolveClass<CheckAccess::kYes>(dex::TypeIndex(inst->VRegB_21c())); if (res_type.IsConflict()) { DCHECK_NE(failures_.size(), 0U); break; // bad class @@ -2675,7 +2677,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { // ensure that subsequent merges don't lose type information - such as becoming an // interface from a class that would lose information relevant to field checks. const RegType& orig_type = work_line_->GetRegisterType(this, instance_of_inst->VRegB_22c()); - const RegType& cast_type = ResolveClassAndCheckAccess( + const RegType& cast_type = ResolveClass<CheckAccess::kYes>( dex::TypeIndex(instance_of_inst->VRegC_22c())); if (!orig_type.Equals(cast_type) && @@ -3723,7 +3725,8 @@ inline bool MethodVerifier::IsInstantiableOrPrimitive(mirror::Class* klass) { return klass->IsInstantiable() || klass->IsPrimitive(); } -const RegType& MethodVerifier::ResolveClassAndCheckAccess(dex::TypeIndex class_idx) { +template <MethodVerifier::CheckAccess C> +const RegType& MethodVerifier::ResolveClass(dex::TypeIndex class_idx) { mirror::Class* klass = can_load_classes_ ? Runtime::Current()->GetClassLinker()->ResolveType( *dex_file_, class_idx, dex_cache_, class_loader_) @@ -3760,13 +3763,15 @@ const RegType& MethodVerifier::ResolveClassAndCheckAccess(dex::TypeIndex class_i // Record result of class resolution attempt. VerifierDeps::MaybeRecordClassResolution(*dex_file_, class_idx, klass); - // Check if access is allowed. Unresolved types use xxxWithAccessCheck to - // check at runtime if access is allowed and so pass here. If result is - // primitive, skip the access check. - if (result->IsNonZeroReferenceTypes() && !result->IsUnresolvedTypes()) { + // If requested, check if access is allowed. Unresolved types are included in this check, as the + // interpreter only tests whether access is allowed when a class is not pre-verified and runs in + // the access-checks interpreter. If result is primitive, skip the access check. + // + // Note: we do this for unresolved classes to trigger re-verification at runtime. + if (C == CheckAccess::kYes && result->IsNonZeroReferenceTypes()) { const RegType& referrer = GetDeclaringClass(); - if (!referrer.IsUnresolvedTypes() && !referrer.CanAccess(*result)) { - Fail(VERIFY_ERROR_ACCESS_CLASS) << "illegal class access: '" + if (!referrer.CanAccess(*result)) { + Fail(VERIFY_ERROR_ACCESS_CLASS) << "(possibly) illegal class access: '" << referrer << "' -> '" << *result << "'"; } } @@ -3785,7 +3790,8 @@ const RegType& MethodVerifier::GetCaughtExceptionType() { if (!iterator.GetHandlerTypeIndex().IsValid()) { common_super = ®_types_.JavaLangThrowable(false); } else { - const RegType& exception = ResolveClassAndCheckAccess(iterator.GetHandlerTypeIndex()); + const RegType& exception = + ResolveClass<CheckAccess::kYes>(iterator.GetHandlerTypeIndex()); if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(exception, this)) { DCHECK(!exception.IsUninitializedTypes()); // Comes from dex, shouldn't be uninit. if (exception.IsUnresolvedTypes()) { @@ -3827,7 +3833,7 @@ const RegType& MethodVerifier::GetCaughtExceptionType() { ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess( uint32_t dex_method_idx, MethodType method_type) { const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx); - const RegType& klass_type = ResolveClassAndCheckAccess(method_id.class_idx_); + const RegType& klass_type = ResolveClass<CheckAccess::kYes>(method_id.class_idx_); if (klass_type.IsConflict()) { std::string append(" in attempt to access method "); append += dex_file_->GetMethodName(method_id); @@ -4598,7 +4604,7 @@ void MethodVerifier::VerifyNewArray(const Instruction* inst, bool is_filled, boo DCHECK_EQ(inst->Opcode(), Instruction::FILLED_NEW_ARRAY_RANGE); type_idx = dex::TypeIndex(inst->VRegB_3rc()); } - const RegType& res_type = ResolveClassAndCheckAccess(type_idx); + const RegType& res_type = ResolveClass<CheckAccess::kYes>(type_idx); if (res_type.IsConflict()) { // bad class DCHECK_NE(failures_.size(), 0U); } else { @@ -4812,7 +4818,7 @@ void MethodVerifier::VerifyAPut(const Instruction* inst, ArtField* 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_); + const RegType& klass_type = ResolveClass<CheckAccess::kYes>(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), @@ -4820,6 +4826,9 @@ ArtField* MethodVerifier::GetStaticField(int field_idx) { return nullptr; } if (klass_type.IsUnresolvedTypes()) { + // Accessibility checks depend on resolved fields. + DCHECK(klass_type.Equals(GetDeclaringClass()) || !failures_.empty()); + return nullptr; // Can't resolve Class so no more to do here, will do checking at runtime. } ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); @@ -4850,7 +4859,7 @@ ArtField* MethodVerifier::GetStaticField(int field_idx) { ArtField* 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_); + const RegType& klass_type = ResolveClass<CheckAccess::kYes>(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), @@ -4858,6 +4867,9 @@ ArtField* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_id return nullptr; } if (klass_type.IsUnresolvedTypes()) { + // Accessibility checks depend on resolved fields. + DCHECK(klass_type.Equals(GetDeclaringClass()) || !failures_.empty()); + return nullptr; // Can't resolve Class so no more to do here } ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); @@ -4994,6 +5006,31 @@ void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, const RegType& DCHECK(!can_load_classes_ || self_->IsExceptionPending()); self_->ClearException(); } + } else { + // If we don't have the field (it seems we failed resolution) and this is a PUT, we need to + // redo verification at runtime as the field may be final, unless the field id shows it's in + // the same class. + // + // For simplicity, it is OK to not distinguish compile-time vs runtime, and post this an + // ACCESS_FIELD failure at runtime. This has the same effect as NO_FIELD - punting the class + // to the access-checks interpreter. + // + // Note: see b/34966607. This and above may be changed in the future. + if (kAccType == FieldAccessType::kAccPut) { + const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx); + const char* field_class_descriptor = dex_file_->GetFieldDeclaringClassDescriptor(field_id); + const RegType* field_class_type = ®_types_.FromDescriptor(GetClassLoader(), + field_class_descriptor, + false); + if (!field_class_type->Equals(GetDeclaringClass())) { + Fail(VERIFY_ERROR_ACCESS_FIELD) << "could not check field put for final field modify of " + << field_class_descriptor + << "." + << dex_file_->GetFieldName(field_id) + << " from other class " + << GetDeclaringClass(); + } + } } if (field_type == nullptr) { const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx); diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index ea8729cb3e..da4102a786 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -576,9 +576,14 @@ class MethodVerifier { void VerifyQuickFieldAccess(const Instruction* inst, const RegType& insn_type, bool is_primitive) REQUIRES_SHARED(Locks::mutator_lock_); - // Resolves a class based on an index and performs access checks to ensure the referrer can - // access the resolved class. - const RegType& ResolveClassAndCheckAccess(dex::TypeIndex class_idx) + enum class CheckAccess { // private. + kYes, + kNo, + }; + // Resolves a class based on an index and, if C is kYes, performs access checks to ensure + // the referrer can access the resolved class. + template <CheckAccess C> + const RegType& ResolveClass(dex::TypeIndex class_idx) REQUIRES_SHARED(Locks::mutator_lock_); /* diff --git a/test/954-invoke-polymorphic-verifier/smali/Unresolved.smali b/test/954-invoke-polymorphic-verifier/smali/Unresolved.smali index 882f0e9256..26044726ad 100644 --- a/test/954-invoke-polymorphic-verifier/smali/Unresolved.smali +++ b/test/954-invoke-polymorphic-verifier/smali/Unresolved.smali @@ -23,7 +23,7 @@ .line 23 invoke-direct {p0}, Ljava/lang/Object;-><init>()V # Get an unresolvable instance (abstract class) - invoke-static {}, LAbstract;->getUnresolvedInstance()Lother/thing/Foo; + invoke-static {}, LUnresolved;->getUnresolvedInstance()Lother/thing/Foo; move-result-object v0 const-string v1, "1" const-string v2, "2" |