diff options
author | 2019-10-04 20:34:46 +0000 | |
---|---|---|
committer | 2019-10-04 14:21:21 -0700 | |
commit | ea2a3d949354c8b054983ba629c81bc5ff7163da (patch) | |
tree | dc94a9c92b254d3f94d96ad0dc27f195d5a7767c | |
parent | 7fca6ef7b9dbe5a54f2c93a3484035f48ebae27a (diff) |
Revert^2 "Walk internal ArtField/ArtMethod pointers"
This reverts commit 712fa800b2b78e527d36c88dc369bf4b723587ea.
We incorrectly didn't check if a method was obsolete before giving its
class's MethodIds array. We then incorrectly used this array and the
(placeholder) -1 index to try to find the previous method-id. Since -1
is not a valid array index we got check failures. To fix this we
simply added a check that the method is not obsolete and if it is we
go to the slow-path.
Reason for revert: Fixed issue causing out-of-bounds array access
Test: ./test.py --host --debuggable --ndebuggable
Bug: 134162467
Change-Id: Iaffefeab6e889b4fb6554a11452d0af051001cb7
38 files changed, 1289 insertions, 103 deletions
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index 77acd562d1..3f205eb6f4 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -64,6 +64,8 @@ #include "mirror/object-inl.h" #include "monitor-inl.h" #include "nativehelper/scoped_local_ref.h" +#include "reflective_handle.h" +#include "reflective_handle_scope-inl.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "scoped_thread_state_change.h" @@ -800,11 +802,14 @@ class JvmtiMethodTraceListener final : public art::instrumentation::Instrumentat // Call-back for when we read from a field. void FieldRead(art::Thread* self, art::Handle<art::mirror::Object> this_object, - art::ArtMethod* method, + art::ArtMethod* method_p, uint32_t dex_pc, - art::ArtField* field) + art::ArtField* field_p) REQUIRES_SHARED(art::Locks::mutator_lock_) override { if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kFieldAccess)) { + art::StackReflectiveHandleScope<1, 1> rhs(self); + art::ReflectiveHandle<art::ArtField> field(rhs.NewHandle(field_p)); + art::ReflectiveHandle<art::ArtMethod> method(rhs.NewHandle(method_p)); art::JNIEnvExt* jnienv = self->GetJniEnv(); // DCHECK(!self->IsExceptionPending()); ScopedLocalRef<jobject> this_ref(jnienv, AddLocalRef<jobject>(jnienv, this_object.Get())); @@ -824,13 +829,16 @@ class JvmtiMethodTraceListener final : public art::instrumentation::Instrumentat void FieldWritten(art::Thread* self, art::Handle<art::mirror::Object> this_object, - art::ArtMethod* method, + art::ArtMethod* method_p, uint32_t dex_pc, - art::ArtField* field, + art::ArtField* field_p, art::Handle<art::mirror::Object> new_val) REQUIRES_SHARED(art::Locks::mutator_lock_) override { if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kFieldModification)) { art::JNIEnvExt* jnienv = self->GetJniEnv(); + art::StackReflectiveHandleScope<1, 1> rhs(self); + art::ReflectiveHandle<art::ArtField> field(rhs.NewHandle(field_p)); + art::ReflectiveHandle<art::ArtMethod> method(rhs.NewHandle(method_p)); // DCHECK(!self->IsExceptionPending()); ScopedLocalRef<jobject> this_ref(jnienv, AddLocalRef<jobject>(jnienv, this_object.Get())); ScopedLocalRef<jobject> fklass(jnienv, @@ -856,13 +864,16 @@ class JvmtiMethodTraceListener final : public art::instrumentation::Instrumentat // Call-back for when we write into a field. void FieldWritten(art::Thread* self, art::Handle<art::mirror::Object> this_object, - art::ArtMethod* method, + art::ArtMethod* method_p, uint32_t dex_pc, - art::ArtField* field, + art::ArtField* field_p, const art::JValue& field_value) REQUIRES_SHARED(art::Locks::mutator_lock_) override { if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kFieldModification)) { art::JNIEnvExt* jnienv = self->GetJniEnv(); + art::StackReflectiveHandleScope<1, 1> rhs(self); + art::ReflectiveHandle<art::ArtField> field(rhs.NewHandle(field_p)); + art::ReflectiveHandle<art::ArtMethod> method(rhs.NewHandle(method_p)); DCHECK(!self->IsExceptionPending()); ScopedLocalRef<jobject> this_ref(jnienv, AddLocalRef<jobject>(jnienv, this_object.Get())); ScopedLocalRef<jobject> fklass(jnienv, diff --git a/runtime/Android.bp b/runtime/Android.bp index 81e1ecfc87..cfa16f87dc 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -189,6 +189,7 @@ libart_cc_defaults { "read_barrier.cc", "reference_table.cc", "reflection.cc", + "reflective_handle_scope.cc", "reflective_value_visitor.cc", "runtime.cc", "runtime_callbacks.cc", diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h index 2f86fbcca6..882e3ce4c7 100644 --- a/runtime/common_dex_operations.h +++ b/runtime/common_dex_operations.h @@ -35,6 +35,8 @@ #include "mirror/class.h" #include "mirror/object.h" #include "obj_ptr-inl.h" +#include "reflective_handle.h" +#include "reflective_handle_scope.h" #include "runtime.h" #include "stack.h" #include "thread.h" @@ -100,8 +102,10 @@ static ALWAYS_INLINE bool DoFieldGetCommon(Thread* self, instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); if (UNLIKELY(instrumentation->HasFieldReadListeners())) { StackHandleScope<1> hs(self); + StackArtFieldHandleScope<1> rhs(self); // Wrap in handle wrapper in case the listener does thread suspension. HandleWrapperObjPtr<mirror::Object> h(hs.NewHandleWrapper(&obj)); + ReflectiveHandleWrapper<ArtField> fh(rhs.NewReflectiveHandleWrapper(&field)); ObjPtr<mirror::Object> this_object; if (!field->IsStatic()) { this_object = obj; @@ -159,8 +163,10 @@ ALWAYS_INLINE bool DoFieldPutCommon(Thread* self, instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); if (UNLIKELY(instrumentation->HasFieldWriteListeners())) { StackHandleScope<2> hs(self); + StackArtFieldHandleScope<1> rhs(self); // Save this and return value (if needed) in case the instrumentation causes a suspend. HandleWrapperObjPtr<mirror::Object> h(hs.NewHandleWrapper(&obj)); + ReflectiveHandleWrapper<ArtField> fh(rhs.NewReflectiveHandleWrapper(&field)); ObjPtr<mirror::Object> this_object = field->IsStatic() ? nullptr : obj; mirror::Object* fake_root = nullptr; HandleWrapper<mirror::Object> ret(hs.NewHandleWrapper<mirror::Object>( @@ -210,8 +216,10 @@ ALWAYS_INLINE bool DoFieldPutCommon(Thread* self, ObjPtr<mirror::Class> field_class; { StackHandleScope<2> hs(self); + StackArtFieldHandleScope<1> rhs(self); HandleWrapperObjPtr<mirror::Object> h_reg(hs.NewHandleWrapper(®)); HandleWrapperObjPtr<mirror::Object> h_obj(hs.NewHandleWrapper(&obj)); + ReflectiveHandleWrapper<ArtField> fh(rhs.NewReflectiveHandleWrapper(&field)); field_class = field->ResolveType(); } // ArtField::ResolveType() may fail as evidenced with a dexing bug (b/78788577). diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 4cc3583496..b28868fb7b 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -71,6 +71,8 @@ #include "oat_file.h" #include "obj_ptr-inl.h" #include "reflection.h" +#include "reflective_handle.h" +#include "reflective_handle_scope-inl.h" #include "runtime-inl.h" #include "scoped_thread_state_change-inl.h" #include "stack.h" @@ -4056,18 +4058,19 @@ void Dbg::ExecuteMethodWithoutPendingException(ScopedObjectAccess& soa, DebugInv soa.Self()->AssertNoPendingException(); // Translate the method through the vtable, unless the debugger wants to suppress it. - ArtMethod* m = pReq->method; + StackArtMethodHandleScope<2> rhs(soa.Self()); + MutableReflectiveHandle<ArtMethod> m(rhs.NewHandle(pReq->method)); PointerSize image_pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); if ((pReq->options & JDWP::INVOKE_NONVIRTUAL) == 0 && pReq->receiver.Read() != nullptr) { - ArtMethod* actual_method = - pReq->klass.Read()->FindVirtualMethodForVirtualOrInterface(m, image_pointer_size); - if (actual_method != m) { - VLOG(jdwp) << "ExecuteMethod translated " << ArtMethod::PrettyMethod(m) - << " to " << ArtMethod::PrettyMethod(actual_method); + MutableReflectiveHandle<ArtMethod> actual_method(rhs.NewHandle( + pReq->klass.Read()->FindVirtualMethodForVirtualOrInterface(m.Get(), image_pointer_size))); + if (actual_method.Get() != m.Get()) { + VLOG(jdwp) << "ExecuteMethod translated " << m->PrettyMethod() + << " to " << actual_method->PrettyMethod(); m = actual_method; } } - VLOG(jdwp) << "ExecuteMethod " << ArtMethod::PrettyMethod(m) + VLOG(jdwp) << "ExecuteMethod " << m->PrettyMethod() << " receiver=" << pReq->receiver.Read() << " arg_count=" << pReq->arg_count; CHECK(m != nullptr); diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 255607937e..fe12a7c379 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -39,6 +39,7 @@ #include "mirror/object-inl.h" #include "mirror/throwable.h" #include "nth_caller_visitor.h" +#include "reflective_handle_scope-inl.h" #include "runtime.h" #include "stack_map.h" #include "thread.h" @@ -381,9 +382,11 @@ inline ArtField* FindFieldFromCode(uint32_t field_idx, return resolved_field; } else { StackHandleScope<1> hs(self); + StackArtFieldHandleScope<1> rhs(self); + ReflectiveHandle<ArtField> resolved_field_handle(rhs.NewHandle(resolved_field)); if (LIKELY(class_linker->EnsureInitialized(self, hs.NewHandle(fields_class), true, true))) { // Otherwise let's ensure the class is initialized before resolving the field. - return resolved_field; + return resolved_field_handle.Get(); } DCHECK(self->IsExceptionPending()); // Throw exception and unwind return nullptr; // Failure. diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index 5b4275cbad..5235f65224 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -136,10 +136,13 @@ class EntrypointsOrderTest : public CommonRuntimeTest { EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, flip_function, method_verifier, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, method_verifier, thread_local_mark_stack, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_mark_stack, async_exception, sizeof(void*)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, async_exception, top_reflective_handle_scope, + sizeof(void*)); // The first field after tlsPtr_ is forced to a 16 byte alignment so it might have some space. auto offset_tlsptr_end = OFFSETOF_MEMBER(Thread, tlsPtr_) + sizeof(decltype(reinterpret_cast<Thread*>(16)->tlsPtr_)); - CHECKED(offset_tlsptr_end - OFFSETOF_MEMBER(Thread, tlsPtr_.async_exception) == sizeof(void*), + CHECKED(offset_tlsptr_end - OFFSETOF_MEMBER(Thread, tlsPtr_.top_reflective_handle_scope) == + sizeof(void*), "async_exception last field"); } diff --git a/runtime/jni/jni_id_manager.cc b/runtime/jni/jni_id_manager.cc index 2553fdf993..4b6335bbbb 100644 --- a/runtime/jni/jni_id_manager.cc +++ b/runtime/jni/jni_id_manager.cc @@ -16,6 +16,10 @@ #include "jni_id_manager.h" +#include <algorithm> +#include <cstdint> +#include <type_traits> + #include "android-base/macros.h" #include "art_field-inl.h" #include "art_method-inl.h" @@ -34,12 +38,11 @@ #include "mirror/class_ext-inl.h" #include "mirror/object-inl.h" #include "obj_ptr-inl.h" +#include "reflective_handle_scope-inl.h" +#include "reflective_handle_scope.h" #include "reflective_value_visitor.h" #include "thread-inl.h" #include "thread.h" -#include <algorithm> -#include <cstdint> -#include <type_traits> namespace art { namespace jni { @@ -60,65 +63,63 @@ static constexpr uintptr_t IndexToId(size_t index) { } template <typename ArtType> -ObjPtr<mirror::PointerArray> GetOrCreateIds(Thread* self, - ObjPtr<mirror::Class> k, - ArtType* t, - /*out*/bool* allocation_failure) +ObjPtr<mirror::PointerArray> GetIds(ObjPtr<mirror::Class> k, ArtType* t) + REQUIRES_SHARED(Locks::mutator_lock_) { + if constexpr (std::is_same_v<ArtType, ArtField>) { + return t->IsStatic() ? k->GetStaticFieldIds() : k->GetInstanceFieldIds(); + } else { + return t->IsObsolete() ? nullptr : k->GetMethodIds(); + } +} + +// Forces the appropriate id array to be present if possible. Returns true if allocation was +// attempted but failed. +template <typename ArtType> +bool EnsureIdsArray(Thread* self, ObjPtr<mirror::Class> k, ArtType* t) REQUIRES_SHARED(Locks::mutator_lock_); template <> -ObjPtr<mirror::PointerArray> GetOrCreateIds(Thread* self, - ObjPtr<mirror::Class> k, - ArtField* field, - /*out*/bool* allocation_failure) { +bool EnsureIdsArray(Thread* self, ObjPtr<mirror::Class> k, ArtField* field) { ScopedExceptionStorage ses(self); StackHandleScope<1> hs(self); Handle<mirror::Class> h_k(hs.NewHandle(k)); - ObjPtr<mirror::PointerArray> res; if (Locks::mutator_lock_->IsExclusiveHeld(self)) { - res = field->IsStatic() ? h_k->GetStaticFieldIds() : h_k->GetInstanceFieldIds(); + return false; } else { - res = field->IsStatic() ? mirror::Class::GetOrCreateStaticFieldIds(h_k) - : mirror::Class::GetOrCreateInstanceFieldIds(h_k); + // NB This modifies the class to allocate the ClassExt and the ids array. + field->IsStatic() ? mirror::Class::GetOrCreateStaticFieldIds(h_k) + : mirror::Class::GetOrCreateInstanceFieldIds(h_k); } if (self->IsExceptionPending()) { self->AssertPendingOOMException(); ses.SuppressOldException("Failed to allocate maps for jmethodIDs. "); - *allocation_failure = true; - } else { - *allocation_failure = false; + return true; } - return res; + return false; } template <> -ObjPtr<mirror::PointerArray> GetOrCreateIds(Thread* self, - ObjPtr<mirror::Class> k, - ArtMethod* method, - /*out*/bool* allocation_failure) { +bool EnsureIdsArray(Thread* self, ObjPtr<mirror::Class> k, ArtMethod* method) { if (method->IsObsolete()) { if (kTraceIds) { LOG(INFO) << "jmethodID for Obsolete method " << method->PrettyMethod() << " requested!"; } // No ids array for obsolete methods. Just do a linear scan. - *allocation_failure = false; - return nullptr; + return false; } StackHandleScope<1> hs(self); Handle<mirror::Class> h_k(hs.NewHandle(k)); - ObjPtr<mirror::PointerArray> res; if (Locks::mutator_lock_->IsExclusiveHeld(self) || !Locks::mutator_lock_->IsSharedHeld(self)) { - res = h_k->GetMethodIds(); + return false; } else { - res = mirror::Class::GetOrCreateMethodIds(h_k); + // NB This modifies the class to allocate the ClassExt and the ids array. + mirror::Class::GetOrCreateMethodIds(h_k); } if (self->IsExceptionPending()) { self->AssertPendingOOMException(); - *allocation_failure = true; - } else { - *allocation_failure = false; + return true; } - return res; + return false; } template <typename ArtType> @@ -135,33 +136,49 @@ size_t GetIdOffset(ObjPtr<mirror::Class> k, ArtMethod* method, PointerSize point // Calls the relevant PrettyMethod/PrettyField on the input. template <typename ArtType> -std::string PrettyGeneric(ArtType* t) REQUIRES_SHARED(Locks::mutator_lock_); -template <> std::string PrettyGeneric(ArtMethod* f) { +std::string PrettyGeneric(ArtType t) REQUIRES_SHARED(Locks::mutator_lock_); +template <> +std::string PrettyGeneric(ArtMethod* f) { + return f->PrettyMethod(); +} +template <> +std::string PrettyGeneric(ReflectiveHandle<ArtMethod> f) { return f->PrettyMethod(); } -template <> std::string PrettyGeneric(ArtField* f) { +template <> +std::string PrettyGeneric(ArtField* f) { + return f->PrettyField(); +} +template <> +std::string PrettyGeneric(ReflectiveHandle<ArtField> f) { return f->PrettyField(); } // Checks if the field or method is obsolete. -template <typename ArtType> bool IsObsolete(ArtType* t) REQUIRES_SHARED(Locks::mutator_lock_); -template <> bool IsObsolete(ArtField* t ATTRIBUTE_UNUSED) { +template <typename ArtType> +bool IsObsolete(ReflectiveHandle<ArtType> t) REQUIRES_SHARED(Locks::mutator_lock_); +template <> +bool IsObsolete(ReflectiveHandle<ArtField> t ATTRIBUTE_UNUSED) { return false; } -template <> bool IsObsolete(ArtMethod* t) { +template <> +bool IsObsolete(ReflectiveHandle<ArtMethod> t) { return t->IsObsolete(); } // Get the canonical (non-copied) version of the field or method. Only relevant for methods. -template <typename ArtType> ArtType* Canonicalize(ArtType* t) REQUIRES_SHARED(Locks::mutator_lock_); -template <> ArtField* Canonicalize(ArtField* t) { - return t; +template <typename ArtType> +ArtType* Canonicalize(ReflectiveHandle<ArtType> t) REQUIRES_SHARED(Locks::mutator_lock_); +template <> +ArtField* Canonicalize(ReflectiveHandle<ArtField> t) { + return t.Get(); } -template <> ArtMethod* Canonicalize(ArtMethod* t) { +template <> +ArtMethod* Canonicalize(ReflectiveHandle<ArtMethod> t) { if (UNLIKELY(t->IsCopied())) { - t = t->GetCanonicalMethod(); + return t->GetCanonicalMethod(); } - return t; + return t.Get(); } }; // namespace @@ -169,7 +186,8 @@ template <> ArtMethod* Canonicalize(ArtMethod* t) { // We increment the id by 2 each time to allow us to use the LSB as a flag that the ID is an index // and not a pointer. This gives us 2**31 unique methods that can be addressed on 32-bit art, which // should be more than enough. -template <> uintptr_t JniIdManager::GetNextId<ArtField>(JniIdType type, ArtField* f) { +template <> +uintptr_t JniIdManager::GetNextId<ArtField>(JniIdType type, ReflectiveHandle<ArtField> f) { if (LIKELY(type == JniIdType::kIndices)) { uintptr_t res = next_field_id_; next_field_id_ += 2; @@ -177,11 +195,12 @@ template <> uintptr_t JniIdManager::GetNextId<ArtField>(JniIdType type, ArtField return res; } else { DCHECK_EQ(type, JniIdType::kSwapablePointer); - return reinterpret_cast<uintptr_t>(f); + return reinterpret_cast<uintptr_t>(f.Get()); } } -template <> uintptr_t JniIdManager::GetNextId<ArtMethod>(JniIdType type, ArtMethod* m) { +template <> +uintptr_t JniIdManager::GetNextId<ArtMethod>(JniIdType type, ReflectiveHandle<ArtMethod> m) { if (LIKELY(type == JniIdType::kIndices)) { uintptr_t res = next_method_id_; next_method_id_ += 2; @@ -189,21 +208,26 @@ template <> uintptr_t JniIdManager::GetNextId<ArtMethod>(JniIdType type, ArtMeth return res; } else { DCHECK_EQ(type, JniIdType::kSwapablePointer); - return reinterpret_cast<uintptr_t>(m); + return reinterpret_cast<uintptr_t>(m.Get()); } } -template <> std::vector<ArtField*>& JniIdManager::GetGenericMap<ArtField>() { +template <> +std::vector<ArtField*>& JniIdManager::GetGenericMap<ArtField>() { return field_id_map_; } -template <> std::vector<ArtMethod*>& JniIdManager::GetGenericMap<ArtMethod>() { +template <> +std::vector<ArtMethod*>& JniIdManager::GetGenericMap<ArtMethod>() { return method_id_map_; } -template <> size_t JniIdManager::GetLinearSearchStartId<ArtField>(ArtField* t ATTRIBUTE_UNUSED) { +template <> +size_t JniIdManager::GetLinearSearchStartId<ArtField>( + ReflectiveHandle<ArtField> t ATTRIBUTE_UNUSED) { return deferred_allocation_field_id_start_; } -template <> size_t JniIdManager::GetLinearSearchStartId<ArtMethod>(ArtMethod* m) { +template <> +size_t JniIdManager::GetLinearSearchStartId<ArtMethod>(ReflectiveHandle<ArtMethod> m) { if (m->IsObsolete()) { return 1; } else { @@ -212,20 +236,24 @@ template <> size_t JniIdManager::GetLinearSearchStartId<ArtMethod>(ArtMethod* m) } // TODO need to fix races in here with visitors -template <typename ArtType> uintptr_t JniIdManager::EncodeGenericId(ArtType* t) { +template <typename ArtType> +uintptr_t JniIdManager::EncodeGenericId(ReflectiveHandle<ArtType> t) { + static_assert(std::is_same_v<ArtType, ArtField> || std::is_same_v<ArtType, ArtMethod>, + "Expected ArtField or ArtMethod"); Runtime* runtime = Runtime::Current(); JniIdType id_type = runtime->GetJniIdType(); if (id_type == JniIdType::kPointer || t == nullptr) { - return reinterpret_cast<uintptr_t>(t); + return reinterpret_cast<uintptr_t>(t.Get()); } Thread* self = Thread::Current(); ScopedExceptionStorage ses(self); - t = Canonicalize(t); ObjPtr<mirror::Class> klass = t->GetDeclaringClass(); DCHECK(!klass.IsNull()) << "Null declaring class " << PrettyGeneric(t); - size_t off = GetIdOffset(klass, t, kRuntimePointerSize); - bool allocation_failure = false; - ObjPtr<mirror::PointerArray> ids(GetOrCreateIds(self, klass, t, &allocation_failure)); + size_t off = GetIdOffset(klass, Canonicalize(t), kRuntimePointerSize); + // Here is the earliest point we can suspend. + bool allocation_failure = EnsureIdsArray(self, klass, t.Get()); + klass = t->GetDeclaringClass(); + ObjPtr<mirror::PointerArray> ids(GetIds(klass, t.Get())); if (allocation_failure) { self->AssertPendingOOMException(); ses.SuppressOldException("OOM exception while trying to allocate JNI ids."); @@ -234,14 +262,27 @@ template <typename ArtType> uintptr_t JniIdManager::EncodeGenericId(ArtType* t) uintptr_t cur_id = 0; if (!ids.IsNull()) { DCHECK_GT(ids->GetLength(), static_cast<int32_t>(off)) << " is " << PrettyGeneric(t); + DCHECK_LE(0, static_cast<int32_t>(off)) << " is " << PrettyGeneric(t); cur_id = ids->GetElementPtrSize<uintptr_t>(off, kRuntimePointerSize); } if (cur_id != 0) { return cur_id; } WriterMutexLock mu(self, *Locks::jni_id_lock_); + ScopedAssertNoThreadSuspension sants("EncodeJniId critical section."); // Check the ids array for a racing id. + constexpr std::pair<size_t, size_t> counts { + std::is_same_v<ArtType, ArtField> ? 1 : 0, + std::is_same_v<ArtType, ArtField> ? 0 : 1, + }; + StackReflectiveHandleScope<counts.first, counts.second> hs(self); + t = hs.NewHandle(Canonicalize(t)); if (!ids.IsNull()) { + // It's possible we got suspended and structurally redefined during the EnsureIdsArray. We need + // to get the information again. + ids = GetIds(klass, t.Get()); + off = GetIdOffset(klass, Canonicalize(t), kRuntimePointerSize); + CHECK(!ids.IsNull()); cur_id = ids->GetElementPtrSize<uintptr_t>(off, kRuntimePointerSize); if (cur_id != 0) { // We were racing some other thread and lost. @@ -259,17 +300,18 @@ template <typename ArtType> uintptr_t JniIdManager::EncodeGenericId(ArtType* t) size_t search_start_index = IdToIndex(GetLinearSearchStartId(t)); size_t index = std::count_if(vec.cbegin() + search_start_index, vec.cend(), - [&found, t](const ArtType* candidate) { - found = found || candidate == t; + [&found, &self, t](const ArtType* candidate) { + Locks::mutator_lock_->AssertSharedHeld(self); + found = found || candidate == t.Get(); return !found; }) + search_start_index; if (found) { // We were either racing some other thread and lost or this thread was asked to encode the // same method multiple times while holding the mutator lock. - DCHECK_EQ(vec[index], t) << "Expected: " << PrettyGeneric(vec[index]) << " got " - << PrettyGeneric(t) << " at index " << index - << " (id: " << IndexToId(index) << ")."; + DCHECK_EQ(vec[index], t.Get()) + << "Expected: " << PrettyGeneric(vec[index]) << " got " << PrettyGeneric(t) + << " at index " << index << " (id: " << IndexToId(index) << ")."; return IndexToId(index); } } @@ -280,10 +322,10 @@ template <typename ArtType> uintptr_t JniIdManager::EncodeGenericId(ArtType* t) std::vector<ArtType*>& vec = GetGenericMap<ArtType>(); vec.reserve(cur_index + 1); vec.resize(std::max(vec.size(), cur_index + 1), nullptr); - vec[cur_index] = t; + vec[cur_index] = t.Get(); } else { DCHECK_EQ(cur_id % 2, 0u); - DCHECK_EQ(cur_id, reinterpret_cast<uintptr_t>(t)); + DCHECK_EQ(cur_id, reinterpret_cast<uintptr_t>(t.Get())); } if (ids.IsNull()) { if (kIsDebugBuild && !IsObsolete(t)) { @@ -298,13 +340,24 @@ template <typename ArtType> uintptr_t JniIdManager::EncodeGenericId(ArtType* t) } jfieldID JniIdManager::EncodeFieldId(ArtField* field) { + StackArtFieldHandleScope<1> rhs(Thread::Current()); + return EncodeFieldId(rhs.NewHandle(field)); +} + +jfieldID JniIdManager::EncodeFieldId(ReflectiveHandle<ArtField> field) { auto* res = reinterpret_cast<jfieldID>(EncodeGenericId(field)); if (kTraceIds && field != nullptr) { LOG(INFO) << "Returning " << res << " for field " << field->PrettyField(); } return res; } + jmethodID JniIdManager::EncodeMethodId(ArtMethod* method) { + StackArtMethodHandleScope<1> rhs(Thread::Current()); + return EncodeMethodId(rhs.NewHandle(method)); +} + +jmethodID JniIdManager::EncodeMethodId(ReflectiveHandle<ArtMethod> method) { auto* res = reinterpret_cast<jmethodID>(EncodeGenericId(method)); if (kTraceIds && method != nullptr) { LOG(INFO) << "Returning " << res << " for method " << method->PrettyMethod(); @@ -445,17 +498,70 @@ void JniIdManager::StartDefer() { } } +class JniIdDeferStackReflectiveScope : public BaseReflectiveHandleScope { + public: + JniIdDeferStackReflectiveScope() REQUIRES_SHARED(art::Locks::mutator_lock_) + : BaseReflectiveHandleScope(), methods_(), fields_() { + PushScope(Thread::Current()); + } + + void Initialize(const std::vector<ArtMethod*>& methods, const std::vector<ArtField*>& fields) + REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Roles::uninterruptible_) { + methods_ = methods; + fields_ = fields; + } + + ~JniIdDeferStackReflectiveScope() REQUIRES_SHARED(Locks::mutator_lock_) { + PopScope(); + } + + void VisitTargets(ReflectiveValueVisitor* visitor) override + REQUIRES_SHARED(Locks::mutator_lock_) { + for (auto it = methods_.begin(); it != methods_.end(); ++it) { + if (*it == nullptr) { + continue; + } + *it = visitor->VisitMethod(*it, ReflectiveHandleScopeSourceInfo(this)); + } + for (auto it = fields_.begin(); it != fields_.end(); ++it) { + if (*it == nullptr) { + continue; + } + *it = visitor->VisitField(*it, ReflectiveHandleScopeSourceInfo(this)); + } + } + + ArtField** GetFieldPtr(size_t idx) REQUIRES_SHARED(Locks::mutator_lock_) { + return &fields_[idx]; + } + + ArtMethod** GetMethodPtr(size_t idx) REQUIRES_SHARED(Locks::mutator_lock_) { + return &methods_[idx]; + } + + size_t NumFields() const { + return fields_.size(); + } + size_t NumMethods() const { + return methods_.size(); + } + + private: + std::vector<ArtMethod*> methods_; + std::vector<ArtField*> fields_; +}; + void JniIdManager::EndDefer() { // Fixup the method->id map. Thread* self = Thread::Current(); - auto set_id = [&](auto* t, uintptr_t id) REQUIRES_SHARED(Locks::mutator_lock_) { + auto set_id = [&](auto** t, uintptr_t id) REQUIRES_SHARED(Locks::mutator_lock_) { if (t == nullptr) { return; } - ObjPtr<mirror::Class> klass(t->GetDeclaringClass()); - size_t off = GetIdOffset(klass, t, kRuntimePointerSize); - bool alloc_failure = false; - ObjPtr<mirror::PointerArray> ids = GetOrCreateIds(self, klass, t, &alloc_failure); + bool alloc_failure = EnsureIdsArray(self, (*t)->GetDeclaringClass(), *t); + ObjPtr<mirror::Class> klass((*t)->GetDeclaringClass()); + size_t off = GetIdOffset(klass, (*t), kRuntimePointerSize); + ObjPtr<mirror::PointerArray> ids = GetIds(klass, (*t)); CHECK(!alloc_failure) << "Could not allocate jni ids array!"; if (ids.IsNull()) { return; @@ -482,25 +588,24 @@ void JniIdManager::EndDefer() { // ids when it finishes. Locks::mutator_lock_->AssertNotExclusiveHeld(self); Locks::mutator_lock_->AssertSharedHeld(self); - std::vector<ArtMethod*> method_id_copy; - std::vector<ArtField*> field_id_copy; + JniIdDeferStackReflectiveScope jidsrs; uintptr_t method_start_id; uintptr_t field_start_id; { ReaderMutexLock mu(self, *Locks::jni_id_lock_); - method_id_copy = method_id_map_; - field_id_copy = field_id_map_; + ScopedAssertNoThreadSuspension sants(__FUNCTION__); + jidsrs.Initialize(method_id_map_, field_id_map_); method_start_id = deferred_allocation_method_id_start_; field_start_id = deferred_allocation_field_id_start_; } - for (size_t index = kIsDebugBuild ? 0 : IdToIndex(method_start_id); index < method_id_copy.size(); + for (size_t index = kIsDebugBuild ? 0 : IdToIndex(method_start_id); index < jidsrs.NumMethods(); ++index) { - set_id(method_id_copy[index], IndexToId(index)); + set_id(jidsrs.GetMethodPtr(index), IndexToId(index)); } - for (size_t index = kIsDebugBuild ? 0 : IdToIndex(field_start_id); index < field_id_copy.size(); + for (size_t index = kIsDebugBuild ? 0 : IdToIndex(field_start_id); index < jidsrs.NumFields(); ++index) { - set_id(field_id_copy[index], IndexToId(index)); + set_id(jidsrs.GetFieldPtr(index), IndexToId(index)); } WriterMutexLock mu(self, *Locks::jni_id_lock_); DCHECK_GE(deferred_allocation_refcount_, 1u); diff --git a/runtime/jni/jni_id_manager.h b/runtime/jni/jni_id_manager.h index 6b43534d18..1cfcefb858 100644 --- a/runtime/jni/jni_id_manager.h +++ b/runtime/jni/jni_id_manager.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_JNI_JNI_ID_MANAGER_H_ #define ART_RUNTIME_JNI_JNI_ID_MANAGER_H_ -#include <atomic> #include <jni.h> + +#include <atomic> #include <vector> #include "art_field.h" @@ -28,6 +29,8 @@ #include "reflective_value_visitor.h" namespace art { +template<typename RT> class ReflectiveHandle; + namespace jni { class ScopedEnableSuspendAllJniIdQueries; @@ -42,8 +45,12 @@ class JniIdManager { ArtMethod* DecodeMethodId(jmethodID method) REQUIRES(!Locks::jni_id_lock_); ArtField* DecodeFieldId(jfieldID field) REQUIRES(!Locks::jni_id_lock_); + jmethodID EncodeMethodId(ReflectiveHandle<ArtMethod> method) REQUIRES(!Locks::jni_id_lock_) + REQUIRES_SHARED(Locks::mutator_lock_); jmethodID EncodeMethodId(ArtMethod* method) REQUIRES(!Locks::jni_id_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + jfieldID EncodeFieldId(ReflectiveHandle<ArtField> field) REQUIRES(!Locks::jni_id_lock_) + REQUIRES_SHARED(Locks::mutator_lock_); jfieldID EncodeFieldId(ArtField* field) REQUIRES(!Locks::jni_id_lock_) REQUIRES_SHARED(Locks::mutator_lock_); @@ -52,15 +59,20 @@ class JniIdManager { private: template <typename ArtType> - uintptr_t EncodeGenericId(ArtType* t) REQUIRES(!Locks::jni_id_lock_) + uintptr_t EncodeGenericId(ReflectiveHandle<ArtType> t) REQUIRES(!Locks::jni_id_lock_) REQUIRES_SHARED(Locks::mutator_lock_); template <typename ArtType> ArtType* DecodeGenericId(uintptr_t input) REQUIRES(!Locks::jni_id_lock_); - template <typename ArtType> std::vector<ArtType*>& GetGenericMap() REQUIRES(Locks::jni_id_lock_); + template <typename ArtType> std::vector<ArtType*>& GetGenericMap() + REQUIRES(Locks::jni_id_lock_); template <typename ArtType> - uintptr_t GetNextId(JniIdType id, ArtType* t) REQUIRES(Locks::jni_id_lock_); + uintptr_t GetNextId(JniIdType id, ReflectiveHandle<ArtType> t) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(Locks::jni_id_lock_); template <typename ArtType> - size_t GetLinearSearchStartId(ArtType* t) REQUIRES(Locks::jni_id_lock_); + size_t GetLinearSearchStartId(ReflectiveHandle<ArtType> t) + REQUIRES(Locks::jni_id_lock_) + REQUIRES_SHARED(Locks::mutator_lock_); void StartDefer() REQUIRES(!Locks::jni_id_lock_) REQUIRES_SHARED(Locks::mutator_lock_); void EndDefer() REQUIRES(!Locks::jni_id_lock_) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/jni/jni_internal.h b/runtime/jni/jni_internal.h index da1792231b..1616ee5263 100644 --- a/runtime/jni/jni_internal.h +++ b/runtime/jni/jni_internal.h @@ -22,7 +22,10 @@ #include "base/locks.h" #include "base/macros.h" +#include "reflective_handle.h" +#include "reflective_handle_scope.h" #include "runtime.h" +#include "thread.h" namespace art { @@ -87,6 +90,16 @@ static inline ArtField* DecodeArtField(jfieldID fid) { } template <bool kEnableIndexIds = true> +ALWAYS_INLINE static inline jfieldID EncodeArtField(ReflectiveHandle<ArtField> field) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (kEnableIndexIds && Runtime::Current()->GetJniIdType() != JniIdType::kPointer) { + return Runtime::Current()->GetJniIdManager()->EncodeFieldId(field); + } else { + return reinterpret_cast<jfieldID>(field.Get()); + } +} + +template <bool kEnableIndexIds = true> ALWAYS_INLINE static inline jfieldID EncodeArtField(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) { if (kEnableIndexIds && Runtime::Current()->GetJniIdType() != JniIdType::kPointer) { @@ -98,6 +111,17 @@ static inline jfieldID EncodeArtField(ArtField* field) REQUIRES_SHARED(Locks::mu template <bool kEnableIndexIds = true> ALWAYS_INLINE +static inline jmethodID EncodeArtMethod(ReflectiveHandle<ArtMethod> art_method) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (kEnableIndexIds && Runtime::Current()->GetJniIdType() != JniIdType::kPointer) { + return Runtime::Current()->GetJniIdManager()->EncodeMethodId(art_method); + } else { + return reinterpret_cast<jmethodID>(art_method.Get()); + } +} + +template <bool kEnableIndexIds = true> +ALWAYS_INLINE static inline jmethodID EncodeArtMethod(ArtMethod* art_method) REQUIRES_SHARED(Locks::mutator_lock_) { if (kEnableIndexIds && Runtime::Current()->GetJniIdType() != JniIdType::kPointer) { diff --git a/runtime/reflective_handle.h b/runtime/reflective_handle.h new file mode 100644 index 0000000000..014d976025 --- /dev/null +++ b/runtime/reflective_handle.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_REFLECTIVE_HANDLE_H_ +#define ART_RUNTIME_REFLECTIVE_HANDLE_H_ + +#include "base/value_object.h" +#include "reflective_reference.h" + +namespace art { + +// This is a holder similar to Handle<T> that is used to hold reflective references to ArtField and +// ArtMethod structures. A reflective reference is one that must be updated if the underlying class +// or instances are replaced due to structural redefinition or some other process. In general these +// don't need to be used. It's only when it's important that a reference to a field not become +// obsolete and it needs to be held over a suspend point that this should be used. +template <typename T> +class ReflectiveHandle : public ValueObject { + public: + static_assert(std::is_same_v<T, ArtField> || std::is_same_v<T, ArtMethod>, + "Expected ArtField or ArtMethod"); + + ReflectiveHandle() : reference_(nullptr) {} + + ALWAYS_INLINE ReflectiveHandle(const ReflectiveHandle<T>& handle) = default; + ALWAYS_INLINE ReflectiveHandle<T>& operator=(const ReflectiveHandle<T>& handle) = default; + + ALWAYS_INLINE explicit ReflectiveHandle(ReflectiveReference<T>* reference) + : reference_(reference) {} + + ALWAYS_INLINE T& operator*() const REQUIRES_SHARED(Locks::mutator_lock_) { + return *Get(); + } + + ALWAYS_INLINE T* operator->() const REQUIRES_SHARED(Locks::mutator_lock_) { + return Get(); + } + + ALWAYS_INLINE T* Get() const REQUIRES_SHARED(Locks::mutator_lock_) { + return reference_->Ptr(); + } + + ALWAYS_INLINE bool IsNull() const { + // It's safe to null-check it without a read barrier. + return reference_->IsNull(); + } + + ALWAYS_INLINE bool operator!=(std::nullptr_t) const REQUIRES_SHARED(Locks::mutator_lock_) { + return !IsNull(); + } + + ALWAYS_INLINE bool operator==(std::nullptr_t) const REQUIRES_SHARED(Locks::mutator_lock_) { + return IsNull(); + } + + protected: + ReflectiveReference<T>* reference_; + + private: + friend class BaseReflectiveHandleScope; + template <size_t kNumFieldReferences, size_t kNumMethodReferences> + friend class StackReflectiveHandleScope; +}; + +// Handles that support assignment. +template <typename T> +class MutableReflectiveHandle : public ReflectiveHandle<T> { + public: + MutableReflectiveHandle() {} + + ALWAYS_INLINE MutableReflectiveHandle(const MutableReflectiveHandle<T>& handle) + REQUIRES_SHARED(Locks::mutator_lock_) = default; + + ALWAYS_INLINE MutableReflectiveHandle<T>& operator=(const MutableReflectiveHandle<T>& handle) + REQUIRES_SHARED(Locks::mutator_lock_) = default; + + ALWAYS_INLINE explicit MutableReflectiveHandle(ReflectiveReference<T>* reference) + REQUIRES_SHARED(Locks::mutator_lock_) + : ReflectiveHandle<T>(reference) {} + + ALWAYS_INLINE T* Assign(T* reference) REQUIRES_SHARED(Locks::mutator_lock_) { + ReflectiveReference<T>* ref = ReflectiveHandle<T>::reference_; + T* old = ref->Ptr(); + ref->Assign(reference); + return old; + } + + private: + friend class BaseReflectiveHandleScope; + template <size_t kNumFieldReferences, size_t kNumMethodReferences> + friend class StackReflectiveHandleScope; +}; + +template<typename T> +class ReflectiveHandleWrapper : public MutableReflectiveHandle<T> { + public: + ReflectiveHandleWrapper(T** obj, const MutableReflectiveHandle<T>& handle) + : MutableReflectiveHandle<T>(handle), obj_(obj) { + } + + ReflectiveHandleWrapper(const ReflectiveHandleWrapper&) = default; + + ~ReflectiveHandleWrapper() { + *obj_ = MutableReflectiveHandle<T>::Get(); + } + + private: + T** const obj_; +}; + +} // namespace art + +#endif // ART_RUNTIME_REFLECTIVE_HANDLE_H_ diff --git a/runtime/reflective_handle_scope-inl.h b/runtime/reflective_handle_scope-inl.h new file mode 100644 index 0000000000..64ea9f921a --- /dev/null +++ b/runtime/reflective_handle_scope-inl.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_REFLECTIVE_HANDLE_SCOPE_INL_H_ +#define ART_RUNTIME_REFLECTIVE_HANDLE_SCOPE_INL_H_ + +#include "android-base/thread_annotations.h" +#include "base/mutex.h" +#include "reflective_handle.h" +#include "reflective_handle_scope.h" +#include "thread-current-inl.h" + +namespace art { + +template <size_t kNumFields, size_t kNumMethods> +StackReflectiveHandleScope<kNumFields, kNumMethods>::StackReflectiveHandleScope(Thread* self) : field_pos_(0), method_pos_(0) { + DCHECK_EQ(self, Thread::Current()); + PushScope(self); +} + +template <size_t kNumFields, size_t kNumMethods> +void StackReflectiveHandleScope<kNumFields, kNumMethods>::VisitTargets( + ReflectiveValueVisitor* visitor) { + Thread* self = Thread::Current(); + DCHECK(GetThread() == self || + Locks::mutator_lock_->IsExclusiveHeld(self)) + << *GetThread() << " on thread " << *self; + auto visit_one = [&](auto& rv) NO_THREAD_SAFETY_ANALYSIS { + Locks::mutator_lock_->AssertSharedHeld(self); + if (!rv.IsNull()) { + rv.Assign((*visitor)(rv.Ptr(), ReflectiveHandleScopeSourceInfo(this))); + } + }; + std::for_each(fields_.begin(), fields_.begin() + field_pos_, visit_one); + std::for_each(methods_.begin(), methods_.begin() + method_pos_, visit_one); +} + +template <size_t kNumFields, size_t kNumMethods> +StackReflectiveHandleScope<kNumFields, kNumMethods>::~StackReflectiveHandleScope() { + PopScope(); +} + +void BaseReflectiveHandleScope::PushScope(Thread* self) { + DCHECK_EQ(self, Thread::Current()); + self_ = self; + link_ = self_->GetTopReflectiveHandleScope(); + self_->PushReflectiveHandleScope(this); +} + +void BaseReflectiveHandleScope::PopScope() { + auto* prev = self_->PopReflectiveHandleScope(); + CHECK_EQ(prev, this); + link_ = nullptr; +} + +} // namespace art + +#endif // ART_RUNTIME_REFLECTIVE_HANDLE_SCOPE_INL_H_ diff --git a/runtime/reflective_handle_scope.cc b/runtime/reflective_handle_scope.cc new file mode 100644 index 0000000000..2c3ae5e6c5 --- /dev/null +++ b/runtime/reflective_handle_scope.cc @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "reflective_handle_scope.h" + +#include <ostream> + +#include "thread.h" + +namespace art { + + +void BaseReflectiveHandleScope::Describe(std::ostream& os) const { + os << "[BaseReflectiveHandleScope self_=" << *self_ << ", link_=" << link_ << "]"; +} + +std::ostream& operator<<(std::ostream& os, const BaseReflectiveHandleScope& brhs) { + brhs.Describe(os); + return os; +} + +} // namespace art diff --git a/runtime/reflective_handle_scope.h b/runtime/reflective_handle_scope.h new file mode 100644 index 0000000000..46cff8bce7 --- /dev/null +++ b/runtime/reflective_handle_scope.h @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_REFLECTIVE_HANDLE_SCOPE_H_ +#define ART_RUNTIME_REFLECTIVE_HANDLE_SCOPE_H_ + +#include <android-base/logging.h> + +#include <array> +#include <compare> +#include <functional> +#include <stack> + +#include "android-base/macros.h" +#include "base/enums.h" +#include "base/globals.h" +#include "base/locks.h" +#include "base/macros.h" +#include "base/value_object.h" +#include "reflective_handle.h" +#include "reflective_reference.h" +#include "reflective_value_visitor.h" + +namespace art { + +class ArtField; +class ArtMethod; +class BaseReflectiveHandleScope; +class Thread; + +// This is a holder similar to StackHandleScope that is used to hold reflective references to +// ArtField and ArtMethod structures. A reflective reference is one that must be updated if the +// underlying class or instances are replaced due to structural redefinition or some other process. +// In general these don't need to be used. It's only when it's important that a reference to a field +// not become obsolete and it needs to be held over a suspend point that this should be used. This +// takes care of the book-keeping to allow the runtime to visit and update ReflectiveHandles when +// structural redefinition occurs. +class BaseReflectiveHandleScope { + public: + template <typename Visitor> + ALWAYS_INLINE void VisitTargets(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_) { + FunctionReflectiveValueVisitor v(&visitor); + VisitTargets(&v); + } + + ALWAYS_INLINE virtual ~BaseReflectiveHandleScope() { + DCHECK(link_ == nullptr); + } + + virtual void VisitTargets(ReflectiveValueVisitor* visitor) + REQUIRES_SHARED(Locks::mutator_lock_) = 0; + + BaseReflectiveHandleScope* GetLink() { + return link_; + } + + Thread* GetThread() { + return self_; + } + + void Describe(std::ostream& os) const; + + protected: + ALWAYS_INLINE BaseReflectiveHandleScope() : self_(nullptr), link_(nullptr) {} + + ALWAYS_INLINE inline void PushScope(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); + ALWAYS_INLINE inline void PopScope() REQUIRES_SHARED(Locks::mutator_lock_); + + // Thread this node is rooted in. + Thread* self_; + // Next node in the handle-scope linked list. Root is held by Thread. + BaseReflectiveHandleScope* link_; + + private: + DISALLOW_COPY_AND_ASSIGN(BaseReflectiveHandleScope); +}; +std::ostream& operator<<(std::ostream& os, const BaseReflectiveHandleScope& brhs); + +template <size_t kNumFields, size_t kNumMethods> +class StackReflectiveHandleScope : public BaseReflectiveHandleScope { + private: + static constexpr bool kHasFields = kNumFields > 0; + static constexpr bool kHasMethods = kNumMethods > 0; + + public: + ALWAYS_INLINE explicit StackReflectiveHandleScope(Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_); + ALWAYS_INLINE ~StackReflectiveHandleScope() REQUIRES_SHARED(Locks::mutator_lock_); + + void VisitTargets(ReflectiveValueVisitor* visitor) override REQUIRES_SHARED(Locks::mutator_lock_); + + template <typename T, + typename = typename std::enable_if_t<(kHasFields && std::is_same_v<T, ArtField>) || + (kHasMethods && std::is_same_v<T, ArtMethod>)>> + ALWAYS_INLINE MutableReflectiveHandle<T> NewHandle(T* t) REQUIRES_SHARED(Locks::mutator_lock_) { + if constexpr (std::is_same_v<T, ArtField>) { + return NewFieldHandle(t); + } else { + static_assert(std::is_same_v<T, ArtMethod>, "Expected ArtField or ArtMethod"); + return NewMethodHandle(t); + } + } + template<typename T> + ALWAYS_INLINE ReflectiveHandleWrapper<T> NewReflectiveHandleWrapper(T** t) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return ReflectiveHandleWrapper<T>(t, NewHandle(*t)); + } + + ALWAYS_INLINE MutableReflectiveHandle<ArtField> NewFieldHandle(ArtField* f) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + static_assert(kHasFields, "No fields"); + DCHECK_LT(field_pos_, kNumFields); + MutableReflectiveHandle<ArtField> fh(GetMutableFieldHandle(field_pos_++)); + fh.Assign(f); + return fh; + } + ALWAYS_INLINE ReflectiveHandleWrapper<ArtField> NewReflectiveFieldHandleWrapper(ArtField** f) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return ReflectiveHandleWrapper<ArtField>(f, NewMethodHandle(*f)); + } + + ALWAYS_INLINE ArtField* GetField(size_t i) { + static_assert(kHasFields, "No fields"); + return GetFieldReference(i)->Ptr(); + } + ALWAYS_INLINE ReflectiveHandle<ArtField> GetFieldHandle(size_t i) { + static_assert(kHasFields, "No fields"); + return ReflectiveHandle<ArtField>(GetFieldReference(i)); + } + ALWAYS_INLINE MutableReflectiveHandle<ArtField> GetMutableFieldHandle(size_t i) { + static_assert(kHasFields, "No fields"); + return MutableReflectiveHandle<ArtField>(GetFieldReference(i)); + } + + ALWAYS_INLINE MutableReflectiveHandle<ArtMethod> NewMethodHandle(ArtMethod* m) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + static_assert(kHasMethods, "No methods"); + DCHECK_LT(method_pos_, kNumMethods); + MutableReflectiveHandle<ArtMethod> mh(GetMutableMethodHandle(method_pos_++)); + mh.Assign(m); + return mh; + } + ALWAYS_INLINE ReflectiveHandleWrapper<ArtMethod> NewReflectiveMethodHandleWrapper(ArtMethod** m) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return ReflectiveHandleWrapper<ArtMethod>(m, NewMethodHandle(*m)); + } + + ALWAYS_INLINE ArtMethod* GetMethod(size_t i) { + static_assert(kHasMethods, "No methods"); + return GetMethodReference(i)->Ptr(); + } + ALWAYS_INLINE ReflectiveHandle<ArtMethod> GetMethodHandle(size_t i) { + static_assert(kHasMethods, "No methods"); + return ReflectiveHandle<ArtMethod>(GetMethodReference(i)); + } + ALWAYS_INLINE MutableReflectiveHandle<ArtMethod> GetMutableMethodHandle(size_t i) { + static_assert(kHasMethods, "No methods"); + return MutableReflectiveHandle<ArtMethod>(GetMethodReference(i)); + } + + size_t RemainingFieldSlots() const { + return kNumFields - field_pos_; + } + + size_t RemainingMethodSlots() const { + return kNumMethods - method_pos_; + } + + private: + ReflectiveReference<ArtMethod>* GetMethodReference(size_t i) { + DCHECK_LT(i, method_pos_); + return &methods_[i]; + } + + ReflectiveReference<ArtField>* GetFieldReference(size_t i) { + DCHECK_LT(i, field_pos_); + return &fields_[i]; + } + + size_t field_pos_; + size_t method_pos_; + std::array<ReflectiveReference<ArtField>, kNumFields> fields_; + std::array<ReflectiveReference<ArtMethod>, kNumMethods> methods_; +}; + +template <size_t kNumMethods> +using StackArtMethodHandleScope = StackReflectiveHandleScope</*kNumFields=*/0, kNumMethods>; + +template <size_t kNumFields> +using StackArtFieldHandleScope = StackReflectiveHandleScope<kNumFields, /*kNumMethods=*/0>; + +} // namespace art + +#endif // ART_RUNTIME_REFLECTIVE_HANDLE_SCOPE_H_ diff --git a/runtime/reflective_reference.h b/runtime/reflective_reference.h new file mode 100644 index 0000000000..f57c0301bc --- /dev/null +++ b/runtime/reflective_reference.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_REFLECTIVE_REFERENCE_H_ +#define ART_RUNTIME_REFLECTIVE_REFERENCE_H_ + +#include "android-base/macros.h" +#include "base/macros.h" +#include "mirror/object_reference.h" + +namespace art { + +class ArtField; +class ArtMethod; +// A reference to a ArtField or ArtMethod. +template <class ReflectiveType> +class ReflectiveReference { + public: + static_assert(std::is_same_v<ReflectiveType, ArtMethod> || + std::is_same_v<ReflectiveType, ArtField>, + "Uknown type!"); + ReflectiveReference() : val_(nullptr) {} + explicit ReflectiveReference(ReflectiveType* r) : val_(r) {} + ReflectiveReference<ReflectiveType>& operator=(const ReflectiveReference<ReflectiveType>& t) = + default; + + ReflectiveType* Ptr() { + return val_; + } + + void Assign(ReflectiveType* r) { + val_ = r; + } + + bool IsNull() const { + return val_ == nullptr; + } + + bool operator==(const ReflectiveReference<ReflectiveType>& rr) const { + return val_ == rr.val_; + } + bool operator!=(const ReflectiveReference<ReflectiveType>& rr) const { + return !operator==(rr); + } + bool operator==(std::nullptr_t) const { + return IsNull(); + } + bool operator!=(std::nullptr_t) const { + return !IsNull(); + } + + private: + ReflectiveType* val_; +}; + +} // namespace art + +#endif // ART_RUNTIME_REFLECTIVE_REFERENCE_H_ diff --git a/runtime/reflective_value_visitor.cc b/runtime/reflective_value_visitor.cc index 69fd51ff6e..5a288d38be 100644 --- a/runtime/reflective_value_visitor.cc +++ b/runtime/reflective_value_visitor.cc @@ -42,4 +42,13 @@ void JniIdReflectiveSourceInfo<jmethodID>::Describe(std::ostream& os) const { os << " jmethodID=" << reinterpret_cast<uintptr_t>(id_); } +void ReflectiveHandleScopeSourceInfo::Describe(std::ostream& os) const { + ReflectionSourceInfo::Describe(os); + os << " source= (" << source_ << ") "; + if (source_ == nullptr) { + os << "nullptr"; + } else { + os << *source_; + } +} } // namespace art diff --git a/runtime/reflective_value_visitor.h b/runtime/reflective_value_visitor.h index 8823fcba9c..0b09a0bf8a 100644 --- a/runtime/reflective_value_visitor.h +++ b/runtime/reflective_value_visitor.h @@ -124,10 +124,7 @@ class ReflectiveHandleScopeSourceInfo : public ReflectionSourceInfo { explicit ReflectiveHandleScopeSourceInfo(BaseReflectiveHandleScope* source) : ReflectionSourceInfo(kSourceThreadHandleScope), source_(source) {} - void Describe(std::ostream& os) const override { - ReflectionSourceInfo::Describe(os); - os << " source=" << source_; - } + void Describe(std::ostream& os) const override; private: BaseReflectiveHandleScope* source_; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 8f68b55a26..0c9aeceb15 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -2198,6 +2198,7 @@ void Runtime::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) { } void Runtime::VisitReflectiveTargets(ReflectiveValueVisitor *visitor) { + thread_list_->VisitReflectiveTargets(visitor); heap_->VisitReflectiveTargets(visitor); jni_id_manager_->VisitReflectiveTargets(visitor); callbacks_->VisitReflectiveTargets(visitor); diff --git a/runtime/thread.cc b/runtime/thread.cc index 088f997855..3aacfcb8dd 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -98,6 +98,7 @@ #include "quick_exception_handler.h" #include "read_barrier-inl.h" #include "reflection.h" +#include "reflective_handle_scope-inl.h" #include "runtime-inl.h" #include "runtime.h" #include "runtime_callbacks.h" @@ -3957,6 +3958,14 @@ class RootCallbackVisitor { const uint32_t tid_; }; +void Thread::VisitReflectiveTargets(ReflectiveValueVisitor* visitor) { + for (BaseReflectiveHandleScope* brhs = GetTopReflectiveHandleScope(); + brhs != nullptr; + brhs = brhs->GetLink()) { + brhs->VisitTargets(visitor); + } +} + template <bool kPrecise> void Thread::VisitRoots(RootVisitor* visitor) { const pid_t thread_id = GetThreadId(); diff --git a/runtime/thread.h b/runtime/thread.h index 22c9d06da5..16ca2aa4f6 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -40,6 +40,7 @@ #include "managed_stack.h" #include "offsets.h" #include "read_barrier_config.h" +#include "reflective_handle_scope.h" #include "runtime_globals.h" #include "runtime_stats.h" #include "thread_state.h" @@ -635,6 +636,9 @@ class Thread { void VisitRoots(RootVisitor* visitor, VisitRootFlags flags) REQUIRES_SHARED(Locks::mutator_lock_); + void VisitReflectiveTargets(ReflectiveValueVisitor* visitor) + REQUIRES(Locks::mutator_lock_); + void VerifyStack() REQUIRES_SHARED(Locks::mutator_lock_) { if (kVerifyStack) { VerifyStackImpl(); @@ -892,6 +896,23 @@ class Thread { top_handle_scope)); } + BaseReflectiveHandleScope* GetTopReflectiveHandleScope() { + return tlsPtr_.top_reflective_handle_scope; + } + + void PushReflectiveHandleScope(BaseReflectiveHandleScope* scope) { + DCHECK_EQ(scope->GetLink(), tlsPtr_.top_reflective_handle_scope); + DCHECK_EQ(scope->GetThread(), this); + tlsPtr_.top_reflective_handle_scope = scope; + } + + BaseReflectiveHandleScope* PopReflectiveHandleScope() { + BaseReflectiveHandleScope* handle_scope = tlsPtr_.top_reflective_handle_scope; + DCHECK(handle_scope != nullptr); + tlsPtr_.top_reflective_handle_scope = tlsPtr_.top_reflective_handle_scope->GetLink(); + return handle_scope; + } + DebugInvokeReq* GetInvokeReq() const { return tlsPtr_.debug_invoke_req; } @@ -1639,7 +1660,7 @@ class Thread { thread_local_objects(0), mterp_current_ibase(nullptr), thread_local_alloc_stack_top(nullptr), thread_local_alloc_stack_end(nullptr), flip_function(nullptr), method_verifier(nullptr), thread_local_mark_stack(nullptr), - async_exception(nullptr) { + async_exception(nullptr), top_reflective_handle_scope(nullptr) { std::fill(held_mutexes, held_mutexes + kLockLevelCount, nullptr); } @@ -1797,6 +1818,9 @@ class Thread { // The pending async-exception or null. mirror::Throwable* async_exception; + + // Top of the linked-list for reflective-handle scopes or null if none. + BaseReflectiveHandleScope* top_reflective_handle_scope; } tlsPtr_; // Small thread-local cache to be used from the interpreter. diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 28bc184595..4e8f468346 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -1542,6 +1542,13 @@ void ThreadList::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) const { } } +void ThreadList::VisitReflectiveTargets(ReflectiveValueVisitor *visitor) const { + MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); + for (const auto& thread : list_) { + thread->VisitReflectiveTargets(visitor); + } +} + uint32_t ThreadList::AllocThreadId(Thread* self) { MutexLock mu(self, *Locks::allocated_thread_ids_lock_); for (size_t i = 0; i < allocated_ids_.size(); ++i) { diff --git a/runtime/thread_list.h b/runtime/thread_list.h index b5b4450498..dad896eb5a 100644 --- a/runtime/thread_list.h +++ b/runtime/thread_list.h @@ -22,6 +22,7 @@ #include "base/mutex.h" #include "base/value_object.h" #include "jni.h" +#include "reflective_handle_scope.h" #include "suspend_reason.h" #include <bitset> @@ -173,6 +174,8 @@ class ThreadList { REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + void VisitReflectiveTargets(ReflectiveValueVisitor* visitor) const REQUIRES(Locks::mutator_lock_); + // Return a copy of the thread list. std::list<Thread*> GetList() REQUIRES(Locks::thread_list_lock_) { return list_; diff --git a/test/1985-structural-redefine-stack-scope/expected.txt b/test/1985-structural-redefine-stack-scope/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/1985-structural-redefine-stack-scope/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/1985-structural-redefine-stack-scope/info.txt b/test/1985-structural-redefine-stack-scope/info.txt new file mode 100644 index 0000000000..a108013047 --- /dev/null +++ b/test/1985-structural-redefine-stack-scope/info.txt @@ -0,0 +1 @@ +Tests StackReflectiveHandleScope. diff --git a/test/1985-structural-redefine-stack-scope/run b/test/1985-structural-redefine-stack-scope/run new file mode 100755 index 0000000000..a36de16ea6 --- /dev/null +++ b/test/1985-structural-redefine-stack-scope/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Ask for stack traces to be dumped to a file rather than to stdout. +./default-run "$@" --jvmti --android-runtime-option -Xopaque-jni-ids:true diff --git a/test/1985-structural-redefine-stack-scope/src/Main.java b/test/1985-structural-redefine-stack-scope/src/Main.java new file mode 100644 index 0000000000..cb86c3d2ec --- /dev/null +++ b/test/1985-structural-redefine-stack-scope/src/Main.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import art.Redefinition; +import java.lang.invoke.*; +import java.lang.reflect.Field; +import java.util.Base64; + +public class Main { + public static final class Transform { + static { + } + + public static Object foo = null; + } + + /** + * Base64 encoded dex bytes for: + * public static final class Transform { + * static {} + * public static Object bar = null; + * public static Object foo = null; + * } + */ + public static final byte[] DEX_BYTES = + Base64.getDecoder() + .decode( + "ZGV4CjAzNQCjkRjcSr1RJO8FnnCjHV/8h6keJP/+P3WQAwAAcAAAAHhWNBIAAAAAAAAAANgCAAAQ" + + "AAAAcAAAAAYAAACwAAAAAQAAAMgAAAACAAAA1AAAAAMAAADkAAAAAQAAAPwAAAB0AgAAHAEAAFwB" + + "AABmAQAAbgEAAIABAACIAQAArAEAAMwBAADgAQAA6wEAAPYBAAD5AQAABgIAAAsCAAAQAgAAFgIA" + + "AB0CAAACAAAAAwAAAAQAAAAFAAAABgAAAAkAAAAJAAAABQAAAAAAAAAAAAQACwAAAAAABAAMAAAA" + + "AAAAAAAAAAAAAAAAAQAAAAQAAAABAAAAAAAAABEAAAAEAAAAAAAAAAcAAADIAgAApAIAAAAAAAAB" + + "AAAAAAAAAFABAAAGAAAAEgBpAAAAaQABAA4AAQABAAEAAABVAQAABAAAAHAQAgAAAA4ABwAOPAAF" + + "AA4AAAAACDxjbGluaXQ+AAY8aW5pdD4AEExNYWluJFRyYW5zZm9ybTsABkxNYWluOwAiTGRhbHZp" + + "ay9hbm5vdGF0aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xh" + + "c3M7ABJMamF2YS9sYW5nL09iamVjdDsACU1haW4uamF2YQAJVHJhbnNmb3JtAAFWAAthY2Nlc3NG" + + "bGFncwADYmFyAANmb28ABG5hbWUABXZhbHVlAHZ+fkQ4eyJjb21waWxhdGlvbi1tb2RlIjoiZGVi" + + "dWciLCJtaW4tYXBpIjoxLCJzaGEtMSI6IjI4YmNlZjUwYWM4NTk3Y2YyMmU4OTJiMWJjM2EzYjky" + + "Yjc0ZTcwZTkiLCJ2ZXJzaW9uIjoiMS42LjMyLWRldiJ9AAICAQ4YAQIDAgoEGQ0XCAIAAgAACQEJ" + + "AIiABJwCAYGABLgCAAAAAAIAAACVAgAAmwIAALwCAAAAAAAAAAAAAAAAAAAPAAAAAAAAAAEAAAAA" + + "AAAAAQAAABAAAABwAAAAAgAAAAYAAACwAAAAAwAAAAEAAADIAAAABAAAAAIAAADUAAAABQAAAAMA" + + "AADkAAAABgAAAAEAAAD8AAAAASAAAAIAAAAcAQAAAyAAAAIAAABQAQAAAiAAABAAAABcAQAABCAA" + + "AAIAAACVAgAAACAAAAEAAACkAgAAAxAAAAIAAAC4AgAABiAAAAEAAADIAgAAABAAAAEAAADYAgAA"); + + public static void assertEquals(Object a, Object b) { + if (a != b) { + throw new Error("Expected " + b + ", got " + a); + } + } + + public static void main(String[] args) throws Exception, Throwable { + System.loadLibrary(args[0]); + Field f = Transform.class.getDeclaredField("foo"); + Transform.foo = "THIS IS A FOO VALUE"; + assertEquals(f.get(null), Transform.foo); + MethodHandle j = + NativeFieldScopeCheck( + f, + () -> { + Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES); + }); + assertEquals(j.invokeExact(), Transform.foo); + } + + // Hold the field as a ArtField, run the 'test' function, turn the ArtField into a MethodHandle + // directly and return that. + public static native MethodHandle NativeFieldScopeCheck(Field in, Runnable test); +} diff --git a/test/1985-structural-redefine-stack-scope/src/art/Redefinition.java b/test/1985-structural-redefine-stack-scope/src/art/Redefinition.java new file mode 120000 index 0000000000..81eaf31bbb --- /dev/null +++ b/test/1985-structural-redefine-stack-scope/src/art/Redefinition.java @@ -0,0 +1 @@ +../../../jvmti-common/Redefinition.java
\ No newline at end of file diff --git a/test/1985-structural-redefine-stack-scope/stack_scope.cc b/test/1985-structural-redefine-stack-scope/stack_scope.cc new file mode 100644 index 0000000000..5c5215bac0 --- /dev/null +++ b/test/1985-structural-redefine-stack-scope/stack_scope.cc @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <cstdio> +#include <memory> +#include <mutex> +#include <string> +#include <vector> + +#include "class_linker.h" +#include "class_root.h" +#include "jni.h" +#include "jni/jni_internal.h" +#include "mirror/class.h" +#include "mirror/method_handle_impl.h" +#include "mirror/object-inl.h" +#include "mirror/object_array-alloc-inl.h" +#include "reflection.h" +#include "reflective_handle.h" +#include "reflective_handle_scope-inl.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-inl.h" + +namespace art { +namespace Test1985StructuralRedefineStackScope { + +extern "C" JNICALL jobject JNIEXPORT Java_Main_NativeFieldScopeCheck(JNIEnv* env, + jclass, + jobject field, + jobject runnable) { + jfieldID fid = env->FromReflectedField(field); + jclass runnable_klass = env->FindClass("java/lang/Runnable"); + jmethodID run = env->GetMethodID(runnable_klass, "run", "()V"); + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<4> hs(soa.Self()); + StackArtFieldHandleScope<1> fhs(soa.Self()); + StackArtFieldHandleScope<1> bhs(soa.Self()); + ReflectiveHandle<ArtField> rf(fhs.NewHandle(jni::DecodeArtField(fid))); + ReflectiveHandle<ArtField> bf(bhs.NewHandle(jni::DecodeArtField(fid))); + ArtField* pre_ptr = rf.Get(); + { + ScopedThreadSuspension sts(soa.Self(), ThreadState::kNative); + // Upcall to perform redefinition. + env->CallVoidMethod(runnable, run); + } + Handle<mirror::ObjectArray<mirror::Class>> mt_arr( + hs.NewHandle(mirror::ObjectArray<mirror::Class>::Alloc( + soa.Self(), + Runtime::Current()->GetClassLinker()->FindArrayClass(soa.Self(), + GetClassRoot<mirror::Class>()), + 0))); + Handle<mirror::MethodType> mt(hs.NewHandle(mirror::MethodType::Create( + soa.Self(), hs.NewHandle(GetClassRoot<mirror::Object>()), mt_arr))); + Handle<mirror::MethodHandleImpl> mhi(hs.NewHandle( + mirror::MethodHandleImpl::Create(soa.Self(), + reinterpret_cast<uintptr_t>(rf.Get()), + (rf->IsStatic() ? mirror::MethodHandle::Kind::kStaticGet + : mirror::MethodHandle::Kind::kInstanceGet), + mt))); + CHECK_EQ(rf.Get(), bf.Get()) << "rf: " << rf->PrettyField() << " bf: " << bf->PrettyField(); + // TODO Modify this to work for when run doesn't cause a change. + CHECK_NE(pre_ptr, rf.Get()) << "pre_ptr: " << pre_ptr->PrettyField() + << " rf: " << rf->PrettyField(); + CHECK_EQ(fid, jni::EncodeArtField(rf)); + return soa.AddLocalReference<jobject>(mhi.Get()); +} + +} // namespace Test1985StructuralRedefineStackScope +} // namespace art diff --git a/test/1986-structural-redefine-multi-thread-stack-scope/expected.txt b/test/1986-structural-redefine-multi-thread-stack-scope/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/1986-structural-redefine-multi-thread-stack-scope/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/1986-structural-redefine-multi-thread-stack-scope/info.txt b/test/1986-structural-redefine-multi-thread-stack-scope/info.txt new file mode 100644 index 0000000000..184bd7202e --- /dev/null +++ b/test/1986-structural-redefine-multi-thread-stack-scope/info.txt @@ -0,0 +1 @@ +Tests StackReflectiveHandleScope works when there are several all in different threads. diff --git a/test/1986-structural-redefine-multi-thread-stack-scope/run b/test/1986-structural-redefine-multi-thread-stack-scope/run new file mode 100755 index 0000000000..a36de16ea6 --- /dev/null +++ b/test/1986-structural-redefine-multi-thread-stack-scope/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Ask for stack traces to be dumped to a file rather than to stdout. +./default-run "$@" --jvmti --android-runtime-option -Xopaque-jni-ids:true diff --git a/test/1986-structural-redefine-multi-thread-stack-scope/src/Main.java b/test/1986-structural-redefine-multi-thread-stack-scope/src/Main.java new file mode 100644 index 0000000000..23b1656b65 --- /dev/null +++ b/test/1986-structural-redefine-multi-thread-stack-scope/src/Main.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import art.Redefinition; +import java.lang.invoke.*; +import java.lang.reflect.Field; +import java.util.Base64; +import java.util.concurrent.CountDownLatch; + +public class Main { + public static final class Transform { + static { + } + + public static Object foo = null; + } + + /** + * Base64 encoded dex bytes for: + * + * public static final class Transform { + * static {} + * public static Object bar = null; + * public static Object foo = null; + * } + */ + public static final byte[] DEX_BYTES = + Base64.getDecoder() + .decode( + "ZGV4CjAzNQCjkRjcSr1RJO8FnnCjHV/8h6keJP/+P3WQAwAAcAAAAHhWNBIAAAAAAAAAANgCAAAQ" + + "AAAAcAAAAAYAAACwAAAAAQAAAMgAAAACAAAA1AAAAAMAAADkAAAAAQAAAPwAAAB0AgAAHAEAAFwB" + + "AABmAQAAbgEAAIABAACIAQAArAEAAMwBAADgAQAA6wEAAPYBAAD5AQAABgIAAAsCAAAQAgAAFgIA" + + "AB0CAAACAAAAAwAAAAQAAAAFAAAABgAAAAkAAAAJAAAABQAAAAAAAAAAAAQACwAAAAAABAAMAAAA" + + "AAAAAAAAAAAAAAAAAQAAAAQAAAABAAAAAAAAABEAAAAEAAAAAAAAAAcAAADIAgAApAIAAAAAAAAB" + + "AAAAAAAAAFABAAAGAAAAEgBpAAAAaQABAA4AAQABAAEAAABVAQAABAAAAHAQAgAAAA4ABwAOPAAF" + + "AA4AAAAACDxjbGluaXQ+AAY8aW5pdD4AEExNYWluJFRyYW5zZm9ybTsABkxNYWluOwAiTGRhbHZp" + + "ay9hbm5vdGF0aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xh" + + "c3M7ABJMamF2YS9sYW5nL09iamVjdDsACU1haW4uamF2YQAJVHJhbnNmb3JtAAFWAAthY2Nlc3NG" + + "bGFncwADYmFyAANmb28ABG5hbWUABXZhbHVlAHZ+fkQ4eyJjb21waWxhdGlvbi1tb2RlIjoiZGVi" + + "dWciLCJtaW4tYXBpIjoxLCJzaGEtMSI6IjI4YmNlZjUwYWM4NTk3Y2YyMmU4OTJiMWJjM2EzYjky" + + "Yjc0ZTcwZTkiLCJ2ZXJzaW9uIjoiMS42LjMyLWRldiJ9AAICAQ4YAQIDAgoEGQ0XCAIAAgAACQEJ" + + "AIiABJwCAYGABLgCAAAAAAIAAACVAgAAmwIAALwCAAAAAAAAAAAAAAAAAAAPAAAAAAAAAAEAAAAA" + + "AAAAAQAAABAAAABwAAAAAgAAAAYAAACwAAAAAwAAAAEAAADIAAAABAAAAAIAAADUAAAABQAAAAMA" + + "AADkAAAABgAAAAEAAAD8AAAAASAAAAIAAAAcAQAAAyAAAAIAAABQAQAAAiAAABAAAABcAQAABCAA" + + "AAIAAACVAgAAACAAAAEAAACkAgAAAxAAAAIAAAC4AgAABiAAAAEAAADIAgAAABAAAAEAAADYAgAA"); + + public static void assertEquals(Object a, Object b) { + if (a != b) { + throw new Error("Expected " + b + ", got " + a); + } + } + + public static void assertAllEquals(Object[] a, Object b) { + boolean failed = false; + String msg = ""; + for (int i = 0; i < a.length; i++) { + if (a[i] != b) { + failed = true; + msg += "Expected " + b + ", got a[" + i + "] (" + a[i] + "), "; + } + } + if (failed) { + throw new Error(msg); + } + } + + public static void main(String[] args) throws Exception, Throwable { + System.loadLibrary(args[0]); + Field f = Transform.class.getDeclaredField("foo"); + Transform.foo = "THIS IS A FOO VALUE"; + assertEquals(f.get(null), Transform.foo); + final int num_threads = 10; + Object[] results = new Object[num_threads]; + Thread[] threads = new Thread[num_threads]; + CountDownLatch start_latch = new CountDownLatch(num_threads); + CountDownLatch continue_latch = new CountDownLatch(1); + for (int i = 0; i < num_threads; i++) { + final int id = i; + threads[id] = + new Thread( + () -> { + try { + MethodHandle mh = + NativeFieldScopeCheck( + f, + () -> { + try { + start_latch.countDown(); + continue_latch.await(); + } catch (Exception e) { + throw new Error("failed!", e); + } + }); + results[id] = mh.invokeExact(); + } catch (Throwable t) { + throw new Error("Failed", t); + } + }, + "Target thread " + id); + threads[id].start(); + } + start_latch.await(); + Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES); + continue_latch.countDown(); + for (Thread t : threads) { + t.join(); + } + assertAllEquals(results, Transform.foo); + } + + // Hold the field as a ArtField, run the 'test' function, turn the ArtField into a MethodHandle + // directly and return that. + public static native MethodHandle NativeFieldScopeCheck(Field in, Runnable test); +} diff --git a/test/1986-structural-redefine-multi-thread-stack-scope/src/art/Redefinition.java b/test/1986-structural-redefine-multi-thread-stack-scope/src/art/Redefinition.java new file mode 120000 index 0000000000..81eaf31bbb --- /dev/null +++ b/test/1986-structural-redefine-multi-thread-stack-scope/src/art/Redefinition.java @@ -0,0 +1 @@ +../../../jvmti-common/Redefinition.java
\ No newline at end of file diff --git a/test/1987-structural-redefine-recurisive-stack-scope/expected.txt b/test/1987-structural-redefine-recurisive-stack-scope/expected.txt new file mode 100644 index 0000000000..288bed82b3 --- /dev/null +++ b/test/1987-structural-redefine-recurisive-stack-scope/expected.txt @@ -0,0 +1,12 @@ +JNI_OnLoad called +Foo value is THIS IS A FOO VALUE +Result at depth 0: THIS IS A FOO VALUE +Result at depth 1: THIS IS A FOO VALUE +Result at depth 2: THIS IS A FOO VALUE +Result at depth 3: THIS IS A FOO VALUE +Result at depth 4: THIS IS A FOO VALUE +Result at depth 5: THIS IS A FOO VALUE +Result at depth 6: THIS IS A FOO VALUE +Result at depth 7: THIS IS A FOO VALUE +Result at depth 8: THIS IS A FOO VALUE +Result at depth 9: THIS IS A FOO VALUE diff --git a/test/1987-structural-redefine-recurisive-stack-scope/info.txt b/test/1987-structural-redefine-recurisive-stack-scope/info.txt new file mode 100644 index 0000000000..f841897268 --- /dev/null +++ b/test/1987-structural-redefine-recurisive-stack-scope/info.txt @@ -0,0 +1 @@ +Tests StackReflectiveHandleScope works when there are several all in different recursive frames. diff --git a/test/1987-structural-redefine-recurisive-stack-scope/run b/test/1987-structural-redefine-recurisive-stack-scope/run new file mode 100755 index 0000000000..a36de16ea6 --- /dev/null +++ b/test/1987-structural-redefine-recurisive-stack-scope/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Ask for stack traces to be dumped to a file rather than to stdout. +./default-run "$@" --jvmti --android-runtime-option -Xopaque-jni-ids:true diff --git a/test/1987-structural-redefine-recurisive-stack-scope/src/Main.java b/test/1987-structural-redefine-recurisive-stack-scope/src/Main.java new file mode 100644 index 0000000000..2c53057916 --- /dev/null +++ b/test/1987-structural-redefine-recurisive-stack-scope/src/Main.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import art.Redefinition; +import java.lang.invoke.*; +import java.lang.reflect.Field; +import java.util.Base64; + +public class Main { + public static final class Transform { + static { + } + + public static Object foo = null; + } + + /* Base64 encoded dex bytes for: + * + * public static final class Transform { + * static {} + * public static Object bar = null; + * public static Object foo = null; + * } + */ + public static final byte[] DEX_BYTES = + Base64.getDecoder() + .decode( + "ZGV4CjAzNQCjkRjcSr1RJO8FnnCjHV/8h6keJP/+P3WQAwAAcAAAAHhWNBIAAAAAAAAAANgCAAAQ" + + "AAAAcAAAAAYAAACwAAAAAQAAAMgAAAACAAAA1AAAAAMAAADkAAAAAQAAAPwAAAB0AgAAHAEAAFwB" + + "AABmAQAAbgEAAIABAACIAQAArAEAAMwBAADgAQAA6wEAAPYBAAD5AQAABgIAAAsCAAAQAgAAFgIA" + + "AB0CAAACAAAAAwAAAAQAAAAFAAAABgAAAAkAAAAJAAAABQAAAAAAAAAAAAQACwAAAAAABAAMAAAA" + + "AAAAAAAAAAAAAAAAAQAAAAQAAAABAAAAAAAAABEAAAAEAAAAAAAAAAcAAADIAgAApAIAAAAAAAAB" + + "AAAAAAAAAFABAAAGAAAAEgBpAAAAaQABAA4AAQABAAEAAABVAQAABAAAAHAQAgAAAA4ABwAOPAAF" + + "AA4AAAAACDxjbGluaXQ+AAY8aW5pdD4AEExNYWluJFRyYW5zZm9ybTsABkxNYWluOwAiTGRhbHZp" + + "ay9hbm5vdGF0aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xh" + + "c3M7ABJMamF2YS9sYW5nL09iamVjdDsACU1haW4uamF2YQAJVHJhbnNmb3JtAAFWAAthY2Nlc3NG" + + "bGFncwADYmFyAANmb28ABG5hbWUABXZhbHVlAHZ+fkQ4eyJjb21waWxhdGlvbi1tb2RlIjoiZGVi" + + "dWciLCJtaW4tYXBpIjoxLCJzaGEtMSI6IjI4YmNlZjUwYWM4NTk3Y2YyMmU4OTJiMWJjM2EzYjky" + + "Yjc0ZTcwZTkiLCJ2ZXJzaW9uIjoiMS42LjMyLWRldiJ9AAICAQ4YAQIDAgoEGQ0XCAIAAgAACQEJ" + + "AIiABJwCAYGABLgCAAAAAAIAAACVAgAAmwIAALwCAAAAAAAAAAAAAAAAAAAPAAAAAAAAAAEAAAAA" + + "AAAAAQAAABAAAABwAAAAAgAAAAYAAACwAAAAAwAAAAEAAADIAAAABAAAAAIAAADUAAAABQAAAAMA" + + "AADkAAAABgAAAAEAAAD8AAAAASAAAAIAAAAcAQAAAyAAAAIAAABQAQAAAiAAABAAAABcAQAABCAA" + + "AAIAAACVAgAAACAAAAEAAACkAgAAAxAAAAIAAAC4AgAABiAAAAEAAADIAgAAABAAAAEAAADYAgAA"); + + public static void main(String[] args) throws Exception, Throwable { + System.loadLibrary(args[0]); + Field f = Transform.class.getDeclaredField("foo"); + Transform.foo = "THIS IS A FOO VALUE"; + System.out.println("Foo value is " + f.get(null)); + final int max_depth = 10; + Object[] results = new Object[max_depth]; + Runnable res = + () -> { + Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES); + }; + for (int i = 0; i < max_depth; i++) { + final Runnable next = res; + final int id = i; + res = + () -> { + try { + results[id] = NativeFieldScopeCheck(f, next).invokeExact(); + } catch (Throwable t) { + throw new Error("Failed!", t); + } + }; + } + res.run(); + for (int i = 0; i < max_depth; i++) { + System.out.println("Result at depth " + i + ": " + results[i]); + } + } + + // Hold the field as a ArtField, run the 'test' function, turn the ArtField into a MethodHandle + // directly and return that. + public static native MethodHandle NativeFieldScopeCheck(Field in, Runnable test); +} diff --git a/test/1987-structural-redefine-recurisive-stack-scope/src/art/Redefinition.java b/test/1987-structural-redefine-recurisive-stack-scope/src/art/Redefinition.java new file mode 120000 index 0000000000..81eaf31bbb --- /dev/null +++ b/test/1987-structural-redefine-recurisive-stack-scope/src/art/Redefinition.java @@ -0,0 +1 @@ +../../../jvmti-common/Redefinition.java
\ No newline at end of file diff --git a/test/Android.bp b/test/Android.bp index 66a08e92d0..ac83a5dc15 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -518,6 +518,7 @@ cc_defaults { "1002-notify-startup/startup_interface.cc", "1947-breakpoint-redefine-deopt/check_deopt.cc", "1972-jni-id-swap-indices/jni_id.cc", + "1985-structural-redefine-stack-scope/stack_scope.cc", "common/runtime_state.cc", "common/stack_inspect.cc", ], |