diff options
author | 2020-06-05 18:34:49 +0100 | |
---|---|---|
committer | 2020-06-09 12:28:42 +0000 | |
commit | 1715efa0b46d57d587237829d1c0695aaca2c344 (patch) | |
tree | 61e7013808a8fa9c45384fa8d84bd7f3eb1eaf34 | |
parent | 9922f00cf68aac69209216a0726a45eb6338763c (diff) |
Add a new class status for verified with access checks.
At runtime, we won't run the verifier for those classes, but run with
access checks enabled in the interpreter.
Bug: 157265232
Test: test.py
Change-Id: Ia087c3b6f9fcbd295307333e524945d844ef54dc
-rw-r--r-- | dex2oat/driver/compiler_driver.cc | 16 | ||||
-rw-r--r-- | dex2oat/driver/compiler_driver_test.cc | 3 | ||||
-rw-r--r-- | dex2oat/linker/image_writer.cc | 5 | ||||
-rw-r--r-- | dex2oat/linker/oat_writer.cc | 5 | ||||
-rw-r--r-- | openjdkjvmti/ti_redefine.cc | 1 | ||||
-rw-r--r-- | runtime/aot_class_linker.cc | 3 | ||||
-rw-r--r-- | runtime/class_linker.cc | 94 | ||||
-rw-r--r-- | runtime/class_status.h | 12 | ||||
-rw-r--r-- | runtime/mirror/class.cc | 32 | ||||
-rw-r--r-- | runtime/mirror/class.h | 16 | ||||
-rw-r--r-- | runtime/oat.h | 4 | ||||
-rw-r--r-- | runtime/verifier/method_verifier.cc | 39 | ||||
-rw-r--r-- | runtime/verifier/verifier_enums.h | 1 |
13 files changed, 149 insertions, 82 deletions
diff --git a/dex2oat/driver/compiler_driver.cc b/dex2oat/driver/compiler_driver.cc index 1b29e3b77a..cb4cb274ba 100644 --- a/dex2oat/driver/compiler_driver.cc +++ b/dex2oat/driver/compiler_driver.cc @@ -831,7 +831,10 @@ static void EnsureVerifiedOrVerifyAtRuntime(jobject jclass_loader, if (cls == nullptr) { soa.Self()->ClearException(); } else if (&cls->GetDexFile() == dex_file) { - DCHECK(cls->IsErroneous() || cls->IsVerified() || cls->ShouldVerifyAtRuntime()) + DCHECK(cls->IsErroneous() || + cls->IsVerified() || + cls->ShouldVerifyAtRuntime() || + cls->IsVerifiedNeedsAccessChecks()) << cls->PrettyClass() << " " << cls->GetStatus(); } @@ -2014,7 +2017,8 @@ class VerifyClassVisitor : public CompilationVisitor { // Force a soft failure for the VerifierDeps. This is a sanity measure, as // the vdex file already records that the class hasn't been resolved. It avoids // trying to do future verification optimizations when processing the vdex file. - DCHECK(failure_kind == verifier::FailureKind::kNoFailure) << failure_kind; + DCHECK(failure_kind == verifier::FailureKind::kNoFailure || + failure_kind == verifier::FailureKind::kAccessChecksFailure) << failure_kind; failure_kind = verifier::FailureKind::kSoftFailure; } } else if (&klass->GetDexFile() != &dex_file) { @@ -2043,7 +2047,10 @@ class VerifyClassVisitor : public CompilationVisitor { manager_->GetCompiler()->AddSoftVerifierFailure(); } - CHECK(klass->ShouldVerifyAtRuntime() || klass->IsVerified() || klass->IsErroneous()) + CHECK(klass->ShouldVerifyAtRuntime() || + klass->IsVerifiedNeedsAccessChecks() || + klass->IsVerified() || + klass->IsErroneous()) << klass->PrettyDescriptor() << ": state=" << klass->GetStatus(); // Class has a meaningful status for the compiler now, record it. @@ -2073,6 +2080,8 @@ class VerifyClassVisitor : public CompilationVisitor { } if (klass->IsVerified()) { DCHECK_EQ(failure_kind, verifier::FailureKind::kNoFailure); + } else if (klass->IsVerifiedNeedsAccessChecks()) { + DCHECK_EQ(failure_kind, verifier::FailureKind::kAccessChecksFailure); } else if (klass->ShouldVerifyAtRuntime()) { DCHECK_EQ(failure_kind, verifier::FailureKind::kSoftFailure); } else { @@ -2875,6 +2884,7 @@ void CompilerDriver::RecordClassStatus(const ClassReference& ref, ClassStatus st case ClassStatus::kNotReady: case ClassStatus::kResolved: case ClassStatus::kRetryVerificationAtRuntime: + case ClassStatus::kVerifiedNeedsAccessChecks: case ClassStatus::kVerified: case ClassStatus::kSuperclassValidated: case ClassStatus::kVisiblyInitialized: diff --git a/dex2oat/driver/compiler_driver_test.cc b/dex2oat/driver/compiler_driver_test.cc index 50cb292a4d..3096fc31b9 100644 --- a/dex2oat/driver/compiler_driver_test.cc +++ b/dex2oat/driver/compiler_driver_test.cc @@ -353,8 +353,7 @@ TEST_F(CompilerDriverVerifyTest, RetryVerifcationStatusCheckVerified) { ++i) { const ClassStatus expected_status = enum_cast<ClassStatus>(i); // Skip unsupported status that are not supposed to be ever recorded. - if (expected_status == ClassStatus::kVerifyingAtRuntime || - expected_status == ClassStatus::kInitializing || + if (expected_status == ClassStatus::kInitializing || expected_status == ClassStatus::kInitialized) { continue; } diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index d56fea997e..c8f36ccf63 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -3167,6 +3167,11 @@ void ImageWriter::FixupClass(mirror::Class* orig, mirror::Class* copy) { // Remove the clinitThreadId. This is required for image determinism. copy->SetClinitThreadId(static_cast<pid_t>(0)); + // We never emit kRetryVerificationAtRuntime, instead we mark the class as + // resolved and the class will therefore be re-verified at runtime. + if (orig->ShouldVerifyAtRuntime()) { + copy->SetStatusInternal(ClassStatus::kResolved); + } } void ImageWriter::FixupObject(Object* orig, Object* copy) { diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index c005cc23a8..30b61873f9 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -1008,6 +1008,11 @@ class OatWriter::InitOatClassesMethodVisitor : public DexMethodVisitor { status = ClassStatus::kNotReady; } } + // We never emit kRetryVerificationAtRuntime, instead we mark the class as + // resolved and the class will therefore be re-verified at runtime. + if (status == ClassStatus::kRetryVerificationAtRuntime) { + status = ClassStatus::kResolved; + } writer_->oat_class_headers_.emplace_back(offset_, compiled_methods_with_code_, diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc index 707c7009f7..367751f912 100644 --- a/openjdkjvmti/ti_redefine.cc +++ b/openjdkjvmti/ti_redefine.cc @@ -1632,6 +1632,7 @@ bool Redefiner::ClassRedefinition::CheckVerification(const RedefinitionDataIter& // were fixed. It would be nice to reflect this in the new implementations. return true; case art::verifier::FailureKind::kSoftFailure: + case art::verifier::FailureKind::kAccessChecksFailure: // Soft failures might require interpreter on some methods. It won't prevent redefinition but // it does mean we need to run the verifier again and potentially update method flags after // performing the swap. diff --git a/runtime/aot_class_linker.cc b/runtime/aot_class_linker.cc index 6625b8dbd2..a2f450b784 100644 --- a/runtime/aot_class_linker.cc +++ b/runtime/aot_class_linker.cc @@ -115,6 +115,9 @@ verifier::FailureKind AotClassLinker::PerformClassVerification(Thread* self, if (old_status >= ClassStatus::kVerified) { return verifier::FailureKind::kNoFailure; } + if (old_status >= ClassStatus::kVerifiedNeedsAccessChecks) { + return verifier::FailureKind::kAccessChecksFailure; + } // Does it need to be verified at runtime? Report soft failure. if (old_status >= ClassStatus::kRetryVerificationAtRuntime) { // Error messages from here are only reported through -verbose:class. It is not worth it to diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 4b8a94f291..f24c5f48de 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -4574,7 +4574,9 @@ bool ClassLinker::AttemptSupertypeVerification(Thread* self, VerifyClass(self, supertype); } - if (supertype->IsVerified() || supertype->ShouldVerifyAtRuntime()) { + if (supertype->IsVerified() + || supertype->ShouldVerifyAtRuntime() + || supertype->IsVerifiedNeedsAccessChecks()) { // The supertype is either verified, or we soft failed at AOT time. DCHECK(supertype->IsVerified() || Runtime::Current()->IsAotCompiler()); return true; @@ -4614,8 +4616,7 @@ verifier::FailureKind ClassLinker::VerifyClass( // Is somebody verifying this now? ClassStatus old_status = klass->GetStatus(); - while (old_status == ClassStatus::kVerifying || - old_status == ClassStatus::kVerifyingAtRuntime) { + while (old_status == ClassStatus::kVerifying) { lock.WaitIgnoringInterrupts(); // WaitIgnoringInterrupts can still receive an interrupt and return early, in this // case we may see the same status again. b/62912904. This is why the check is @@ -4640,20 +4641,25 @@ verifier::FailureKind ClassLinker::VerifyClass( return verifier::FailureKind::kNoFailure; } + if (klass->IsVerifiedNeedsAccessChecks()) { + if (!Runtime::Current()->IsAotCompiler()) { + // Mark the class as having a verification attempt to avoid re-running + // the verifier and avoid calling EnsureSkipAccessChecksMethods. + klass->SetVerificationAttempted(); + mirror::Class::SetStatus(klass, ClassStatus::kVerified, self); + } + return verifier::FailureKind::kAccessChecksFailure; + } + // For AOT, don't attempt to re-verify if we have already found we should // verify at runtime. - if (Runtime::Current()->IsAotCompiler() && klass->ShouldVerifyAtRuntime()) { + if (klass->ShouldVerifyAtRuntime()) { + CHECK(Runtime::Current()->IsAotCompiler()); return verifier::FailureKind::kSoftFailure; } - if (klass->GetStatus() == ClassStatus::kResolved) { - mirror::Class::SetStatus(klass, ClassStatus::kVerifying, self); - } else { - CHECK_EQ(klass->GetStatus(), ClassStatus::kRetryVerificationAtRuntime) - << klass->PrettyClass(); - CHECK(!Runtime::Current()->IsAotCompiler()); - mirror::Class::SetStatus(klass, ClassStatus::kVerifyingAtRuntime, self); - } + DCHECK_EQ(klass->GetStatus(), ClassStatus::kResolved); + mirror::Class::SetStatus(klass, ClassStatus::kVerifying, self); // Skip verification if disabled. if (!Runtime::Current()->IsVerificationEnabled()) { @@ -4726,7 +4732,8 @@ verifier::FailureKind ClassLinker::VerifyClass( << klass->PrettyDescriptor() << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8() << ": " - << preverified; + << preverified + << "( " << oat_file_class_status << ")"; // If the oat file says the class had an error, re-run the verifier. That way we will get a // precise error message. To ensure a rerun, test: @@ -4755,21 +4762,29 @@ verifier::FailureKind ClassLinker::VerifyClass( if (verifier_failure == verifier::FailureKind::kNoFailure) { // Even though there were no verifier failures we need to respect whether the super-class and // super-default-interfaces were verified or requiring runtime reverification. - if (supertype == nullptr || supertype->IsVerified()) { + if (supertype == nullptr + || supertype->IsVerified() + || supertype->IsVerifiedNeedsAccessChecks()) { mirror::Class::SetStatus(klass, ClassStatus::kVerified, self); } else { + CHECK(Runtime::Current()->IsAotCompiler()); CHECK_EQ(supertype->GetStatus(), ClassStatus::kRetryVerificationAtRuntime); mirror::Class::SetStatus(klass, ClassStatus::kRetryVerificationAtRuntime, self); // Pretend a soft failure occurred so that we don't consider the class verified below. verifier_failure = verifier::FailureKind::kSoftFailure; } } else { - CHECK_EQ(verifier_failure, verifier::FailureKind::kSoftFailure); + CHECK(verifier_failure == verifier::FailureKind::kSoftFailure || + verifier_failure == verifier::FailureKind::kAccessChecksFailure); // Soft failures at compile time should be retried at runtime. Soft // failures at runtime will be handled by slow paths in the generated // code. Set status accordingly. if (Runtime::Current()->IsAotCompiler()) { - mirror::Class::SetStatus(klass, ClassStatus::kRetryVerificationAtRuntime, self); + if (verifier_failure == verifier::FailureKind::kSoftFailure) { + mirror::Class::SetStatus(klass, ClassStatus::kRetryVerificationAtRuntime, self); + } else { + mirror::Class::SetStatus(klass, ClassStatus::kVerifiedNeedsAccessChecks, self); + } } else { mirror::Class::SetStatus(klass, ClassStatus::kVerified, self); // As this is a fake verified status, make sure the methods are _not_ marked @@ -4786,18 +4801,18 @@ verifier::FailureKind ClassLinker::VerifyClass( mirror::Class::SetStatus(klass, ClassStatus::kErrorResolved, self); } if (preverified || verifier_failure == verifier::FailureKind::kNoFailure) { - // Class is verified so we don't need to do any access check on its methods. - // Let the interpreter know it by setting the kAccSkipAccessChecks flag onto each - // method. - // Note: we're going here during compilation and at runtime. When we set the - // kAccSkipAccessChecks flag when compiling image classes, the flag is recorded - // in the image and is set when loading the image. - - if (UNLIKELY(Runtime::Current()->IsVerificationSoftFail())) { + if (oat_file_class_status == ClassStatus::kVerifiedNeedsAccessChecks || + UNLIKELY(Runtime::Current()->IsVerificationSoftFail())) { // Never skip access checks if the verification soft fail is forced. // Mark the class as having a verification attempt to avoid re-running the verifier. klass->SetVerificationAttempted(); } else { + // Class is verified so we don't need to do any access check on its methods. + // Let the interpreter know it by setting the kAccSkipAccessChecks flag onto each + // method. + // Note: we're going here during compilation and at runtime. When we set the + // kAccSkipAccessChecks flag when compiling image classes, the flag is recorded + // in the image and is set when loading the image. EnsureSkipAccessChecksMethods(klass, image_pointer_size_); } } @@ -4850,31 +4865,20 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, if (oat_file_class_status >= ClassStatus::kVerified) { return true; } + if (oat_file_class_status >= ClassStatus::kVerifiedNeedsAccessChecks) { + // We return that the clas has already been verified, and the caller should + // check the class status to ensure we run with access checks. + return true; + } // If we only verified a subset of the classes at compile time, we can end up with classes that // were resolved by the verifier. if (oat_file_class_status == ClassStatus::kResolved) { return false; } - if (oat_file_class_status == ClassStatus::kRetryVerificationAtRuntime) { - // Compile time verification failed with a soft error. Compile time verification can fail - // because we have incomplete type information. Consider the following: - // class ... { - // Foo x; - // .... () { - // if (...) { - // v1 gets assigned a type of resolved class Foo - // } else { - // v1 gets assigned a type of unresolved class Bar - // } - // iput x = v1 - // } } - // when we merge v1 following the if-the-else it results in Conflict - // (see verifier::RegType::Merge) as we can't know the type of Bar and we could possibly be - // allowing an unsafe assignment to the field x in the iput (javac may have compiled this as - // it knew Bar was a sub-class of Foo, but for us this may have been moved into a separate apk - // at compile time). - return false; - } + // We never expect a .oat file to have kRetryVerificationAtRuntime statuses. + CHECK_NE(oat_file_class_status, ClassStatus::kRetryVerificationAtRuntime) + << klass->PrettyClass() << " " << dex_file.GetLocation(); + if (mirror::Class::IsErroneous(oat_file_class_status)) { // Compile time verification failed with a hard error. This is caused by invalid instructions // in the class. These errors are unrecoverable. @@ -5342,7 +5346,7 @@ bool ClassLinker::InitializeClass(Thread* self, VlogClassInitializationFailure(klass); } else { CHECK(Runtime::Current()->IsAotCompiler()); - CHECK_EQ(klass->GetStatus(), ClassStatus::kRetryVerificationAtRuntime); + CHECK(klass->ShouldVerifyAtRuntime() || klass->IsVerifiedNeedsAccessChecks()); self->AssertNoPendingException(); self->SetException(Runtime::Current()->GetPreAllocatedNoClassDefFoundError()); } diff --git a/runtime/class_status.h b/runtime/class_status.h index de64ab7617..b194ffa861 100644 --- a/runtime/class_status.h +++ b/runtime/class_status.h @@ -68,7 +68,15 @@ namespace art { // this state if it encounters a soft failure at compile time. This // often happens when there are unresolved classes in other dex // files, and this status marks a class as needing to be verified -// again at runtime. +// again at runtime. This status is only set and seen during AOT +// compilation, and the compiler will mark the class as resolved in the +// image and/or oat file. +// +// kVerifiedNeedsAccessChecks: The verifier sets a class to +// this state if it encounters access-checks only soft failure at compile +// time. This happens when there are unresolved classes in other dex +// files, and this status marks a class as verified but that will need to run +// with access checks enabled in the interpreter. // // TODO: Explain the other states enum class ClassStatus : uint8_t { @@ -82,7 +90,7 @@ enum class ClassStatus : uint8_t { kResolved = 7, // Part of linking. kVerifying = 8, // In the process of being verified. kRetryVerificationAtRuntime = 9, // Compile time verification failed, retry at runtime. - kVerifyingAtRuntime = 10, // Retrying verification at runtime. + kVerifiedNeedsAccessChecks = 10, // Compile time verification only failed for access checks. kVerified = 11, // Logically part of linking; done pre-init. kSuperclassValidated = 12, // Superclass validation part of init done. kInitializing = 13, // Class init in progress. diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 33ce785a65..6915eb052b 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -218,9 +218,7 @@ static void CheckSetStatus(Thread* self, T thiz, ClassStatus new_status, ClassSt } } -void Class::SetStatusLocked(ClassStatus new_status) { - ClassStatus old_status = GetStatus(); - CheckSetStatus(Thread::Current(), this, new_status, old_status); +void Class::SetStatusInternal(ClassStatus new_status) { if (kBitstringSubtypeCheckEnabled) { // FIXME: This looks broken with respect to aborted transactions. SubtypeCheck<ObjPtr<mirror::Class>>::WriteStatus(this, new_status); @@ -228,11 +226,20 @@ void Class::SetStatusLocked(ClassStatus new_status) { // The ClassStatus is always in the 4 most-significant bits of status_. static_assert(sizeof(status_) == sizeof(uint32_t), "Size of status_ not equal to uint32"); uint32_t new_status_value = static_cast<uint32_t>(new_status) << (32 - kClassStatusBitSize); - DCHECK(!Runtime::Current()->IsActiveTransaction()); - SetField32Volatile<false>(StatusOffset(), new_status_value); + if (Runtime::Current()->IsActiveTransaction()) { + SetField32Volatile<true>(StatusOffset(), new_status_value); + } else { + SetField32Volatile<false>(StatusOffset(), new_status_value); + } } } +void Class::SetStatusLocked(ClassStatus new_status) { + ClassStatus old_status = GetStatus(); + CheckSetStatus(Thread::Current(), this, new_status, old_status); + SetStatusInternal(new_status); +} + void Class::SetStatus(Handle<Class> h_this, ClassStatus new_status, Thread* self) { ClassStatus old_status = h_this->GetStatus(); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); @@ -263,20 +270,7 @@ void Class::SetStatus(Handle<Class> h_this, ClassStatus new_status, Thread* self self->AssertPendingException(); } - if (kBitstringSubtypeCheckEnabled) { - // FIXME: This looks broken with respect to aborted transactions. - ObjPtr<mirror::Class> h_this_ptr = h_this.Get(); - SubtypeCheck<ObjPtr<mirror::Class>>::WriteStatus(h_this_ptr, new_status); - } else { - // The ClassStatus is always in the 4 most-significant bits of status_. - static_assert(sizeof(status_) == sizeof(uint32_t), "Size of status_ not equal to uint32"); - uint32_t new_status_value = static_cast<uint32_t>(new_status) << (32 - kClassStatusBitSize); - if (Runtime::Current()->IsActiveTransaction()) { - h_this->SetField32Volatile<true>(StatusOffset(), new_status_value); - } else { - h_this->SetField32Volatile<false>(StatusOffset(), new_status_value); - } - } + h_this->SetStatusInternal(new_status); // Setting the object size alloc fast path needs to be after the status write so that if the // alloc path sees a valid object size, we would know that it's initialized as long as it has a diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 9b6390e02d..11a5ce528c 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -45,6 +45,10 @@ namespace hiddenapi { class AccessContext; } // namespace hiddenapi +namespace linker { +class ImageWriter; +} // namespace linker + template<typename T> class ArraySlice; class ArtField; class ArtMethod; @@ -169,6 +173,13 @@ class MANAGED Class final : public Object { return GetStatus<kVerifyFlags>() == ClassStatus::kRetryVerificationAtRuntime; } + // Returns true if the class has been verified at compile time, but should be + // executed with access checks. + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + bool IsVerifiedNeedsAccessChecks() REQUIRES_SHARED(Locks::mutator_lock_) { + return GetStatus<kVerifyFlags>() >= ClassStatus::kVerifiedNeedsAccessChecks; + } + // Returns true if the class has been verified. template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> bool IsVerified() REQUIRES_SHARED(Locks::mutator_lock_) { @@ -1397,6 +1408,10 @@ class MANAGED Class final : public Object { void VisitReferences(ObjPtr<Class> klass, const Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_); + // Helper to set the status without any validity cheks. + void SetStatusInternal(ClassStatus new_status) + REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); + // 'Class' Object Fields // Order governed by java field ordering. See art::ClassLinker::LinkFields. @@ -1551,6 +1566,7 @@ class MANAGED Class final : public Object { ART_FRIEND_TEST(DexCacheTest, TestResolvedFieldAccess); // For ResolvedFieldAccessTest friend struct art::ClassOffsets; // for verifying offset information friend class Object; // For VisitReferences + friend class linker::ImageWriter; // For SetStatusInternal DISALLOW_IMPLICIT_CONSTRUCTORS(Class); }; diff --git a/runtime/oat.h b/runtime/oat.h index 0645e4c566..d37927d4bc 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } }; - // Last oat version changed reason: Add requires-image flag. - static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '8', '2', '\0' } }; + // Last oat version changed reason: Change ClassStatus bits with kVerifiedNeedsAccessChecks. + static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '8', '3', '\0' } }; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; static constexpr const char* kDebuggableKey = "debuggable"; diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index c4593f2d0d..59ade97aba 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -5157,6 +5157,19 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self, } } +// Return whether the runtime knows how to execute a method without needing to +// re-verify it at runtime (and therefore save on first use of the class). We +// currently only support it for access checks, where the runtime will mark the +// methods as needing access checks and have the interpreter execute with them. +// The AOT/JIT compiled code is not affected. +static inline bool CanRuntimeHandleVerificationFailure(uint32_t encountered_failure_types) { + constexpr uint32_t unresolved_mask = + verifier::VerifyError::VERIFY_ERROR_ACCESS_CLASS | + verifier::VerifyError::VERIFY_ERROR_ACCESS_FIELD | + verifier::VerifyError::VERIFY_ERROR_ACCESS_METHOD; + return (encountered_failure_types & (~unresolved_mask)) == 0; +} + template <bool kVerifierDebug> MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self, ClassLinker* class_linker, @@ -5219,7 +5232,11 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self, LOG(INFO) << verifier.info_messages_.str(); verifier.Dump(LOG_STREAM(INFO)); } - result.kind = FailureKind::kSoftFailure; + if (CanRuntimeHandleVerificationFailure(verifier.encountered_failure_types_)) { + result.kind = FailureKind::kAccessChecksFailure; + } else { + result.kind = FailureKind::kSoftFailure; + } if (method != nullptr && !CanCompilerHandleVerificationFailure(verifier.encountered_failure_types_)) { set_dont_compile = true; @@ -5512,14 +5529,18 @@ std::ostream& MethodVerifier::Fail(VerifyError error, bool pending_exc) { case VERIFY_ERROR_FORCE_INTERPRETER: case VERIFY_ERROR_LOCKING: if (IsAotMode() || !can_load_classes_) { - // If we're optimistically running verification at compile time, turn NO_xxx, ACCESS_xxx, - // class change and instantiation 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. In these cases, - // we don't want to affect the soundness of the code being compiled. Instead, the - // generated code runs "slow paths" that dynamically perform the verification and cause - // the behavior to be that akin to an interpreter. - error = VERIFY_ERROR_BAD_CLASS_SOFT; + if (error != VERIFY_ERROR_ACCESS_CLASS && + error != VERIFY_ERROR_ACCESS_FIELD && + error != VERIFY_ERROR_ACCESS_METHOD) { + // If we're optimistically running verification at compile time, turn NO_xxx, + // class change and instantiation 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. In these + // cases, we don't want to affect the soundness of the code being compiled. Instead, the + // generated code runs "slow paths" that dynamically perform the verification and cause + // the behavior to be that akin to an interpreter. + error = VERIFY_ERROR_BAD_CLASS_SOFT; + } } else { // If we fail again at runtime, mark that this instruction would throw and force this // method to be executed using the interpreter with checks. diff --git a/runtime/verifier/verifier_enums.h b/runtime/verifier/verifier_enums.h index b445e944a9..33eca4d868 100644 --- a/runtime/verifier/verifier_enums.h +++ b/runtime/verifier/verifier_enums.h @@ -32,6 +32,7 @@ enum class VerifyMode : int8_t { // The outcome of verification. enum class FailureKind { kNoFailure, + kAccessChecksFailure, kSoftFailure, kHardFailure, }; |