From c971eafeff43e4e26959a6e86b62ab0a8f1a6e1c Mon Sep 17 00:00:00 2001 From: Alex Light Date: Tue, 13 Aug 2019 10:50:38 -0700 Subject: Basic structural redefinition support This adds basic support for adding methods and fields to already loaded classes using redefinition. This 'structural class redefinition' is currently limited to classes without any virtual methods or instance fields. One cannot currently structurally redefine multiple classes at once nor will structural redefinition trigger the standard redefinition events. After structural redefinition all references to the old class, and its fields and methods are atomically updated. Any memory associated with the static fields of the old class is zeroed. Offsets for field access might change. If there are any active stack frames for methods from the redefined class the original (obsolete method) code will continue to execute. The identity hash code of the redefined class will not change. Any locks being held, waited or blocked on by the old class will be transferred to the new class. To use this feature the process must be debuggable and running with -Xopaque-jni-ids:true. For device testing use a wrap.sh that adds the following flags: '-Xopaque-jni-ids:true -Xcompiler-option --debuggable -XjdwpProvider:adbconnection' Structural redefinition only available using the "com.android.art.UNSAFE.class.structurally_redefine_class_direct" extension. This will not trigger the normal class-redefinition events. Only one class may be redefined at a time. NB There are still some holes in this potentially allowing obsolete methods/fields to be visible. Most notably during jni-id, MethodHandle and VarHandle creation as well as potentially other places in the runtime. These holes will be closed by later CLs. Until then the extension to access structural class redefinition will remain tagged as UNSAFE. Test: ./test.py --host --all-compiler Bug: 134162467 Change-Id: I825d3a4bdb9594c0147223ae69f433ce9bbfc307 --- runtime/native/java_lang_Class.cc | 90 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 2 deletions(-) (limited to 'runtime/native/java_lang_Class.cc') diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 4967f9eebe..4516d1ba9a 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -218,6 +218,10 @@ static jobjectArray Class_getInterfacesInternal(JNIEnv* env, jobject javaThis) { ScopedFastNativeObjectAccess soa(env); StackHandleScope<1> hs(soa.Self()); Handle klass = hs.NewHandle(DecodeClass(soa, javaThis)); + if (klass->IsObsoleteObject()) { + ThrowRuntimeException("Obsolete Object!"); + return nullptr; + } if (klass->IsProxyClass()) { StackHandleScope<1> hs2(soa.Self()); @@ -262,6 +266,10 @@ static ObjPtr> GetDeclaredFields( ObjPtr klass, bool public_only, bool force_resolve) REQUIRES_SHARED(Locks::mutator_lock_) { + if (UNLIKELY(klass->IsObsoleteObject())) { + ThrowRuntimeException("Obsolete Object!"); + return nullptr; + } StackHandleScope<1> hs(self); IterationRange> ifields = klass->GetIFields(); IterationRange> sfields = klass->GetSFields(); @@ -386,6 +394,10 @@ ALWAYS_INLINE static inline ObjPtr GetDeclaredField(Thread* self, ObjPtr c, ObjPtr name) REQUIRES_SHARED(Locks::mutator_lock_) { + if (UNLIKELY(c->IsObsoleteObject())) { + ThrowRuntimeException("Obsolete Object!"); + return nullptr; + } ArtField* art_field = FindFieldByName(name, c->GetIFieldsPtr()); if (art_field != nullptr) { return mirror::Field::CreateFromArtField(self, art_field, true); @@ -404,6 +416,10 @@ static ObjPtr GetPublicFieldRecursive( DCHECK(name != nullptr); DCHECK(self != nullptr); + if (UNLIKELY(clazz->IsObsoleteObject())) { + ThrowRuntimeException("Obsolete Object!"); + return nullptr; + } StackHandleScope<2> hs(self); MutableHandle h_clazz(hs.NewHandle(clazz)); Handle h_name(hs.NewHandle(name)); @@ -501,10 +517,15 @@ static jobject Class_getDeclaredConstructorInternal( DCHECK(!Runtime::Current()->IsActiveTransaction()); StackHandleScope<1> hs(soa.Self()); + ObjPtr klass = DecodeClass(soa, javaThis); + if (UNLIKELY(klass->IsObsoleteObject())) { + ThrowRuntimeException("Obsolete Object!"); + return nullptr; + } Handle result = hs.NewHandle( mirror::Class::GetDeclaredConstructorInternal( soa.Self(), - DecodeClass(soa, javaThis), + klass, soa.Decode>(args))); if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) { return nullptr; @@ -529,6 +550,10 @@ static jobjectArray Class_getDeclaredConstructorsInternal( bool public_only = (publicOnly != JNI_FALSE); hiddenapi::AccessContext hiddenapi_context = GetReflectionCaller(soa.Self()); Handle h_klass = hs.NewHandle(DecodeClass(soa, javaThis)); + if (UNLIKELY(h_klass->IsObsoleteObject())) { + ThrowRuntimeException("Obsolete Object!"); + return nullptr; + } size_t constructor_count = 0; // Two pass approach for speed. for (auto& m : h_klass->GetDirectMethods(kRuntimePointerSize)) { @@ -563,10 +588,15 @@ static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis, StackHandleScope<1> hs(soa.Self()); DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); DCHECK(!Runtime::Current()->IsActiveTransaction()); + ObjPtr klass = DecodeClass(soa, javaThis); + if (UNLIKELY(klass->IsObsoleteObject())) { + ThrowRuntimeException("Obsolete Object!"); + return nullptr; + } Handle result = hs.NewHandle( mirror::Class::GetDeclaredMethodInternal( soa.Self(), - DecodeClass(soa, javaThis), + klass, soa.Decode(name), soa.Decode>(args), GetHiddenapiAccessContextFunction(soa.Self()))); @@ -585,6 +615,10 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT bool public_only = (publicOnly != JNI_FALSE); Handle klass = hs.NewHandle(DecodeClass(soa, javaThis)); + if (klass->IsObsoleteObject()) { + ThrowRuntimeException("Obsolete Object!"); + return nullptr; + } size_t num_methods = 0; for (ArtMethod& m : klass->GetDeclaredMethods(kRuntimePointerSize)) { uint32_t modifiers = m.GetAccessFlags(); @@ -623,6 +657,10 @@ static jobject Class_getDeclaredAnnotation(JNIEnv* env, jobject javaThis, jclass ScopedFastNativeObjectAccess soa(env); StackHandleScope<2> hs(soa.Self()); Handle klass(hs.NewHandle(DecodeClass(soa, javaThis))); + if (klass->IsObsoleteObject()) { + ThrowRuntimeException("Obsolete Object!"); + return nullptr; + } // Handle public contract to throw NPE if the "annotationClass" argument was null. if (UNLIKELY(annotationClass == nullptr)) { @@ -642,6 +680,10 @@ static jobjectArray Class_getDeclaredAnnotations(JNIEnv* env, jobject javaThis) ScopedFastNativeObjectAccess soa(env); StackHandleScope<1> hs(soa.Self()); Handle klass(hs.NewHandle(DecodeClass(soa, javaThis))); + if (klass->IsObsoleteObject()) { + ThrowRuntimeException("Obsolete Object!"); + return nullptr; + } if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) { // Return an empty array instead of a null pointer. ObjPtr annotation_array_class = @@ -659,6 +701,10 @@ static jobjectArray Class_getDeclaredClasses(JNIEnv* env, jobject javaThis) { ScopedFastNativeObjectAccess soa(env); StackHandleScope<1> hs(soa.Self()); Handle klass(hs.NewHandle(DecodeClass(soa, javaThis))); + if (klass->IsObsoleteObject()) { + ThrowRuntimeException("Obsolete Object!"); + return nullptr; + } ObjPtr> classes = nullptr; if (!klass->IsProxyClass() && klass->GetDexCache() != nullptr) { classes = annotations::GetDeclaredClasses(klass); @@ -682,6 +728,10 @@ static jclass Class_getEnclosingClass(JNIEnv* env, jobject javaThis) { ScopedFastNativeObjectAccess soa(env); StackHandleScope<1> hs(soa.Self()); Handle klass(hs.NewHandle(DecodeClass(soa, javaThis))); + if (klass->IsObsoleteObject()) { + ThrowRuntimeException("Obsolete Object!"); + return nullptr; + } if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) { return nullptr; } @@ -692,6 +742,10 @@ static jobject Class_getEnclosingConstructorNative(JNIEnv* env, jobject javaThis ScopedFastNativeObjectAccess soa(env); StackHandleScope<1> hs(soa.Self()); Handle klass(hs.NewHandle(DecodeClass(soa, javaThis))); + if (klass->IsObsoleteObject()) { + ThrowRuntimeException("Obsolete Object!"); + return nullptr; + } if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) { return nullptr; } @@ -708,6 +762,10 @@ static jobject Class_getEnclosingMethodNative(JNIEnv* env, jobject javaThis) { ScopedFastNativeObjectAccess soa(env); StackHandleScope<1> hs(soa.Self()); Handle klass(hs.NewHandle(DecodeClass(soa, javaThis))); + if (klass->IsObsoleteObject()) { + ThrowRuntimeException("Obsolete Object!"); + return nullptr; + } if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) { return nullptr; } @@ -724,6 +782,10 @@ static jint Class_getInnerClassFlags(JNIEnv* env, jobject javaThis, jint default ScopedFastNativeObjectAccess soa(env); StackHandleScope<1> hs(soa.Self()); Handle klass(hs.NewHandle(DecodeClass(soa, javaThis))); + if (klass->IsObsoleteObject()) { + ThrowRuntimeException("Obsolete Object!"); + return 0; + } return mirror::Class::GetInnerClassFlags(klass, defaultValue); } @@ -731,6 +793,10 @@ static jstring Class_getInnerClassName(JNIEnv* env, jobject javaThis) { ScopedFastNativeObjectAccess soa(env); StackHandleScope<1> hs(soa.Self()); Handle klass(hs.NewHandle(DecodeClass(soa, javaThis))); + if (klass->IsObsoleteObject()) { + ThrowRuntimeException("Obsolete Object!"); + return nullptr; + } if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) { return nullptr; } @@ -745,6 +811,10 @@ static jobjectArray Class_getSignatureAnnotation(JNIEnv* env, jobject javaThis) ScopedFastNativeObjectAccess soa(env); StackHandleScope<1> hs(soa.Self()); Handle klass(hs.NewHandle(DecodeClass(soa, javaThis))); + if (klass->IsObsoleteObject()) { + ThrowRuntimeException("Obsolete Object!"); + return nullptr; + } if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) { return nullptr; } @@ -756,6 +826,10 @@ static jboolean Class_isAnonymousClass(JNIEnv* env, jobject javaThis) { ScopedFastNativeObjectAccess soa(env); StackHandleScope<1> hs(soa.Self()); Handle klass(hs.NewHandle(DecodeClass(soa, javaThis))); + if (klass->IsObsoleteObject()) { + ThrowRuntimeException("Obsolete Object!"); + return 0; + } if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) { return false; } @@ -771,6 +845,10 @@ static jboolean Class_isDeclaredAnnotationPresent(JNIEnv* env, jobject javaThis, ScopedFastNativeObjectAccess soa(env); StackHandleScope<2> hs(soa.Self()); Handle klass(hs.NewHandle(DecodeClass(soa, javaThis))); + if (klass->IsObsoleteObject()) { + ThrowRuntimeException("Obsolete Object!"); + return false; + } if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) { return false; } @@ -782,6 +860,10 @@ static jclass Class_getDeclaringClass(JNIEnv* env, jobject javaThis) { ScopedFastNativeObjectAccess soa(env); StackHandleScope<1> hs(soa.Self()); Handle klass(hs.NewHandle(DecodeClass(soa, javaThis))); + if (klass->IsObsoleteObject()) { + ThrowRuntimeException("Obsolete Object!"); + return nullptr; + } if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) { return nullptr; } @@ -796,6 +878,10 @@ static jobject Class_newInstance(JNIEnv* env, jobject javaThis) { ScopedFastNativeObjectAccess soa(env); StackHandleScope<4> hs(soa.Self()); Handle klass = hs.NewHandle(DecodeClass(soa, javaThis)); + if (klass->IsObsoleteObject()) { + ThrowRuntimeException("Obsolete Object!"); + return nullptr; + } if (UNLIKELY(klass->GetPrimitiveType() != 0 || klass->IsInterface() || klass->IsArrayClass() || klass->IsAbstract())) { soa.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;", -- cgit v1.2.3-59-g8ed1b