diff options
-rw-r--r-- | libdexfile/dex/modifiers.h | 1 | ||||
-rw-r--r-- | runtime/hidden_api.h | 50 | ||||
-rw-r--r-- | runtime/jni/jni_internal.cc | 6 | ||||
-rw-r--r-- | runtime/mirror/class.h | 9 | ||||
-rw-r--r-- | runtime/native/dalvik_system_VMDebug.cc | 20 | ||||
-rw-r--r-- | runtime/native/java_lang_Class.cc | 8 |
6 files changed, 69 insertions, 25 deletions
diff --git a/libdexfile/dex/modifiers.h b/libdexfile/dex/modifiers.h index 2425a588df..be82fff65c 100644 --- a/libdexfile/dex/modifiers.h +++ b/libdexfile/dex/modifiers.h @@ -58,6 +58,7 @@ static constexpr uint32_t kAccObsoleteMethod = 0x00040000; // method (ru static constexpr uint32_t kAccSkipAccessChecks = 0x00080000; // method (runtime, not native) // Used by a class to denote that the verifier has attempted to check it at least once. static constexpr uint32_t kAccVerificationAttempted = 0x00080000; // class (runtime) +static constexpr uint32_t kAccSkipHiddenApiChecks = 0x00100000; // class (runtime) // This is set by the class linker during LinkInterfaceMethods. It is used by a method to represent // that it was copied from its declaring class into another class. All methods marked kAccMiranda // and kAccDefaultConflict will have this bit set. Any kAccDefault method contained in the methods_ diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index 65c6406393..8e21fd3b8f 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -143,31 +143,45 @@ Action GetMemberActionImpl(T* member, // Returns true if the caller is either loaded by the boot strap class loader or comes from // a dex file located in ${ANDROID_ROOT}/framework/. ALWAYS_INLINE -inline bool IsCallerInPlatformDex(ObjPtr<mirror::ClassLoader> caller_class_loader, - ObjPtr<mirror::DexCache> caller_dex_cache) +inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller, + ObjPtr<mirror::ClassLoader> caller_class_loader, + ObjPtr<mirror::DexCache> caller_dex_cache) REQUIRES_SHARED(Locks::mutator_lock_) { if (caller_class_loader.IsNull()) { + // Boot class loader. return true; - } else if (caller_dex_cache.IsNull()) { - return false; - } else { + } + + if (!caller_dex_cache.IsNull()) { const DexFile* caller_dex_file = caller_dex_cache->GetDexFile(); - return caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile(); + if (caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile()) { + // Caller is in a platform dex file. + return true; + } + } + + if (!caller.IsNull() && + caller->ShouldSkipHiddenApiChecks() && + Runtime::Current()->IsJavaDebuggable()) { + // We are in debuggable mode and this caller has been marked trusted. + return true; } + + return false; } } // namespace detail // Returns true if access to `member` should be denied to the caller of the -// reflective query. The decision is based on whether the caller is in the -// platform or not. Because different users of this function determine this -// in a different way, `fn_caller_in_platform(self)` is called and should -// return true if the caller is located in the platform. +// reflective query. The decision is based on whether the caller is trusted or +// not. Because different users of this function determine this in a different +// way, `fn_caller_is_trusted(self)` is called and should return true if the +// caller is allowed to access the platform. // This function might print warnings into the log if the member is hidden. template<typename T> inline Action GetMemberAction(T* member, Thread* self, - std::function<bool(Thread*)> fn_caller_in_platform, + std::function<bool(Thread*)> fn_caller_is_trusted, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(member != nullptr); @@ -188,8 +202,8 @@ inline Action GetMemberAction(T* member, // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access. // This can be *very* expensive. Save it for last. - if (fn_caller_in_platform(self)) { - // Caller in the platform. Exit. + if (fn_caller_is_trusted(self)) { + // Caller is trusted. Exit. return kAllow; } @@ -197,10 +211,9 @@ inline Action GetMemberAction(T* member, return detail::GetMemberActionImpl(member, api_list, action, access_method); } -inline bool IsCallerInPlatformDex(ObjPtr<mirror::Class> caller) - REQUIRES_SHARED(Locks::mutator_lock_) { +inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller) REQUIRES_SHARED(Locks::mutator_lock_) { return !caller.IsNull() && - detail::IsCallerInPlatformDex(caller->GetClassLoader(), caller->GetDexCache()); + detail::IsCallerTrusted(caller, caller->GetClassLoader(), caller->GetDexCache()); } // Returns true if access to `member` should be denied to a caller loaded with @@ -212,10 +225,11 @@ inline Action GetMemberAction(T* member, ObjPtr<mirror::DexCache> caller_dex_cache, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { - bool caller_in_platform = detail::IsCallerInPlatformDex(caller_class_loader, caller_dex_cache); + bool is_caller_trusted = + detail::IsCallerTrusted(/* caller */ nullptr, caller_class_loader, caller_dex_cache); return GetMemberAction(member, /* thread */ nullptr, - [caller_in_platform] (Thread*) { return caller_in_platform; }, + [is_caller_trusted] (Thread*) { return is_caller_trusted; }, access_method); } diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc index 9dbcded867..cd66a60376 100644 --- a/runtime/jni/jni_internal.cc +++ b/runtime/jni/jni_internal.cc @@ -80,15 +80,15 @@ namespace art { // things not rendering correctly. E.g. b/16858794 static constexpr bool kWarnJniAbort = false; -static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - return hiddenapi::IsCallerInPlatformDex(GetCallingClass(self, /* num_frames */ 1)); +static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { + return hiddenapi::IsCallerTrusted(GetCallingClass(self, /* num_frames */ 1)); } template<typename T> ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { hiddenapi::Action action = hiddenapi::GetMemberAction( - member, self, IsCallerInPlatformDex, hiddenapi::kJNI); + member, self, IsCallerTrusted, hiddenapi::kJNI); if (action != hiddenapi::kAllow) { hiddenapi::NotifyHiddenApiListener(member); } diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 51d1376a3c..98e25eb320 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -210,6 +210,15 @@ class MANAGED Class FINAL : public Object { return (GetAccessFlags() & kAccClassIsFinalizable) != 0; } + ALWAYS_INLINE bool ShouldSkipHiddenApiChecks() REQUIRES_SHARED(Locks::mutator_lock_) { + return (GetAccessFlags() & kAccSkipHiddenApiChecks) != 0; + } + + ALWAYS_INLINE void SetSkipHiddenApiChecks() REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t flags = GetAccessFlags(); + SetAccessFlags(flags | kAccSkipHiddenApiChecks); + } + ALWAYS_INLINE void SetRecursivelyInitialized() REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK_EQ(GetLockOwnerThreadId(), Thread::Current()->GetThreadId()); uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_)); diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index 6aaafc2864..f1e267becc 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -588,6 +588,25 @@ static void VMDebug_nativeAttachAgent(JNIEnv* env, jclass, jstring agent, jobjec Runtime::Current()->AttachAgent(env, filename, classloader); } +static void VMDebug_allowHiddenApiReflectionFrom(JNIEnv* env, jclass, jclass j_caller) { + Runtime* runtime = Runtime::Current(); + ScopedObjectAccess soa(env); + + if (!runtime->IsJavaDebuggable()) { + ThrowSecurityException("Can't exempt class, process is not debuggable."); + return; + } + + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::Class> h_caller(hs.NewHandle(soa.Decode<mirror::Class>(j_caller))); + if (h_caller.IsNull()) { + ThrowNullPointerException("argument is null"); + return; + } + + h_caller->SetSkipHiddenApiChecks(); +} + static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"), NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"), @@ -623,6 +642,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"), NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;"), NATIVE_METHOD(VMDebug, nativeAttachAgent, "(Ljava/lang/String;Ljava/lang/ClassLoader;)V"), + NATIVE_METHOD(VMDebug, allowHiddenApiReflectionFrom, "(Ljava/lang/Class;)V"), }; void register_dalvik_system_VMDebug(JNIEnv* env) { diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 2625c0a316..68024cd1c2 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -52,7 +52,7 @@ namespace art { // Returns true if the first caller outside of the Class class or java.lang.invoke package // is in a platform DEX file. -static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { +static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { // Walk the stack and find the first frame not from java.lang.Class and not from java.lang.invoke. // This is very expensive. Save this till the last. struct FirstExternalCallerVisitor : public StackVisitor { @@ -99,7 +99,7 @@ static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_l FirstExternalCallerVisitor visitor(self); visitor.WalkStack(); return visitor.caller != nullptr && - hiddenapi::IsCallerInPlatformDex(visitor.caller->GetDeclaringClass()); + hiddenapi::IsCallerTrusted(visitor.caller->GetDeclaringClass()); } // Returns true if the first non-ClassClass caller up the stack is not allowed to @@ -107,7 +107,7 @@ static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_l ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); - return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerInPlatformDex(self); + return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerTrusted(self); } // Returns true if the first non-ClassClass caller up the stack should not be @@ -116,7 +116,7 @@ template<typename T> ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { hiddenapi::Action action = hiddenapi::GetMemberAction( - member, self, IsCallerInPlatformDex, hiddenapi::kReflection); + member, self, IsCallerTrusted, hiddenapi::kReflection); if (action != hiddenapi::kAllow) { hiddenapi::NotifyHiddenApiListener(member); } |