Fixed issue in structural redefinition around class init
On non-x86 ISAs class initialization is two stage with a class first
being 'kInitialized' and later being 'kVisiblyInitialized'. This has
to do with the memory model of non-x86 ISAs. Because we did not follow
this process correctly we could hit check-failures on non-x86 targets
if the redefined class was not fully initialized when being redefined.
To fix this we force the class-linker to bring the initialization
state of the newly created class all the way to visibly initialized
before allowing the redefinition to take place.
Test: ./test.py --target
Bug: 134162467
Bug: 141236848
Change-Id: I466861270b957a0fe6a90bda0bdabece950f99b2
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index af16b4e..97cf98b 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -422,6 +422,26 @@
}
if (kType == RedefinitionType::kStructural) {
+ // Class initialization interacts really badly with structural redefinition since we need to
+ // make the old class obsolete. We currently just blanket don't allow it.
+ // TODO It might be nice to allow this at some point.
+ if (klass->IsInitializing() &&
+ !klass->IsInitialized() &&
+ klass->GetClinitThreadId() == self->GetTid()) {
+ // We are in the class-init running on this thread.
+ *error_msg = "Modification of class " + klass->PrettyClass() + " during class" +
+ " initialization is not allowed.";
+ return ERR(INTERNAL);
+ }
+ if (!art::Runtime::Current()->GetClassLinker()->EnsureInitialized(
+ self, klass, /*can_init_fields=*/true, /*can_init_parents=*/true)) {
+ self->AssertPendingException();
+ *error_msg = "Class " + klass->PrettyClass() + " failed initialization. Structural" +
+ " redefinition of erroneous classes is not allowed. Failure was: " +
+ self->GetException()->Dump();
+ self->ClearException();
+ return ERR(INVALID_CLASS);
+ }
art::StackHandleScope<2> hs(self);
art::Handle<art::mirror::ObjectArray<art::mirror::Class>> roots(
hs.NewHandle(art::Runtime::Current()->GetClassLinker()->GetClassRoots()));
@@ -1676,7 +1696,11 @@
art::ObjectLock<art::mirror::Class> objlock(driver_->self_, linked_class);
// We already verified the class earlier. No need to do it again.
linked_class->SetVerificationAttempted();
- linked_class->SetStatus(linked_class, art::ClassStatus::kVisiblyInitialized, driver_->self_);
+ // Mark the class as initialized.
+ CHECK(old_class->IsInitialized())
+ << "Attempting to redefine an uninitalized class " << old_class->PrettyClass()
+ << " status=" << old_class->GetStatus();
+ linker->ForceClassInitialized(driver_->self_, linked_class);
// Make sure we have ext-data space for method & field ids. We won't know if we need them until
// it's too late to create them.
// TODO We might want to remove these arrays if they're not needed.
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 10b9da1..93fb8a3 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -387,6 +387,15 @@
}
}
+void ClassLinker::ForceClassInitialized(Thread* self, Handle<mirror::Class> klass) {
+ ClassLinker::VisiblyInitializedCallback* cb = MarkClassInitialized(self, klass);
+ if (cb != nullptr) {
+ cb->MakeVisible(self);
+ }
+ ScopedThreadSuspension sts(self, ThreadState::kSuspended);
+ MakeInitializedClassesVisiblyInitialized(self, /*wait=*/true);
+}
+
ClassLinker::VisiblyInitializedCallback* ClassLinker::MarkClassInitialized(
Thread* self, Handle<mirror::Class> klass) {
if (kRuntimeISA == InstructionSet::kX86 || kRuntimeISA == InstructionSet::kX86_64) {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 792f7b7..a1ba461 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -781,6 +781,12 @@
ClassTable* class_table;
};
+ // Forces a class to be marked as initialized without actually running initializers. Should only
+ // be used by plugin code when creating new classes directly.
+ void ForceClassInitialized(Thread* self, Handle<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
+
protected:
virtual bool InitializeClass(Thread* self,
Handle<mirror::Class> klass,