diff options
Diffstat (limited to 'runtime/hidden_api.h')
-rw-r--r-- | runtime/hidden_api.h | 206 |
1 files changed, 84 insertions, 122 deletions
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index 57f1a599cf..ed00e2a892 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -32,11 +32,10 @@ namespace hiddenapi { // This must be kept in sync with ApplicationInfo.ApiEnforcementPolicy in // frameworks/base/core/java/android/content/pm/ApplicationInfo.java enum class EnforcementPolicy { - kNoChecks = 0, + kDisabled = 0, kJustWarn = 1, // keep checks enabled, but allow everything (enables logging) - kDarkGreyAndBlackList = 2, // ban dark grey & blacklist - kBlacklistOnly = 3, // ban blacklist violations only - kMax = kBlacklistOnly, + kEnabled = 2, // ban dark grey & blacklist + kMax = kEnabled, }; inline EnforcementPolicy EnforcementPolicyFromInt(int api_policy_int) { @@ -45,55 +44,58 @@ inline EnforcementPolicy EnforcementPolicyFromInt(int api_policy_int) { return static_cast<EnforcementPolicy>(api_policy_int); } -enum Action { - kAllow, - kAllowButWarn, - kAllowButWarnAndToast, - kDeny -}; - -enum AccessMethod { +enum class AccessMethod { kNone, // internal test that does not correspond to an actual access by app kReflection, kJNI, kLinking, }; -// Do not change the values of items in this enum, as they are written to the -// event log for offline analysis. Any changes will interfere with that analysis. -enum AccessContextFlags { - // Accessed member is a field if this bit is set, else a method - kMemberIsField = 1 << 0, - // Indicates if access was denied to the member, instead of just printing a warning. - kAccessDenied = 1 << 1, -}; +struct AccessContext { + public: + explicit AccessContext(bool is_trusted) : is_trusted_(is_trusted) {} -inline Action GetActionFromAccessFlags(ApiList api_list) { - if (api_list == ApiList::kWhitelist) { - return kAllow; - } + explicit AccessContext(ObjPtr<mirror::Class> klass) : is_trusted_(GetIsTrusted(klass)) {} - EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); - if (policy == EnforcementPolicy::kNoChecks) { - // Exit early. Nothing to enforce. - return kAllow; - } + AccessContext(ObjPtr<mirror::ClassLoader> class_loader, ObjPtr<mirror::DexCache> dex_cache) + : is_trusted_(GetIsTrusted(class_loader, dex_cache)) {} + + bool IsTrusted() const { return is_trusted_; } + + private: + static bool GetIsTrusted(ObjPtr<mirror::ClassLoader> class_loader, + ObjPtr<mirror::DexCache> dex_cache) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Trust if the caller is in is boot class loader. + if (class_loader.IsNull()) { + return true; + } + + // Trust if caller is in a platform dex file. + if (!dex_cache.IsNull()) { + const DexFile* dex_file = dex_cache->GetDexFile(); + if (dex_file != nullptr && dex_file->IsPlatformDexFile()) { + return true; + } + } - // if policy is "just warn", always warn. We returned above for whitelist APIs. - if (policy == EnforcementPolicy::kJustWarn) { - return kAllowButWarn; + return false; } - DCHECK(policy >= EnforcementPolicy::kDarkGreyAndBlackList); - // The logic below relies on equality of values in the enums EnforcementPolicy and - // ApiList, and their ordering. Assertions are in hidden_api.cc. - if (static_cast<int>(policy) > static_cast<int>(api_list)) { - return api_list == ApiList::kDarkGreylist - ? kAllowButWarnAndToast - : kAllowButWarn; - } else { - return kDeny; + + static bool GetIsTrusted(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(!klass.IsNull()); + + if (klass->ShouldSkipHiddenApiChecks() && Runtime::Current()->IsJavaDebuggable()) { + // Class is known, it is marked trusted and we are in debuggable mode. + return true; + } + + // Check other aspects of the context. + return GetIsTrusted(klass->GetClassLoader(), klass->GetDexCache()); } -} + + bool is_trusted_; +}; class ScopedHiddenApiEnforcementPolicySetting { public: @@ -114,6 +116,13 @@ class ScopedHiddenApiEnforcementPolicySetting { // Implementation details. DO NOT ACCESS DIRECTLY. namespace detail { +enum class SdkCodes { + kVersionNone = std::numeric_limits<int32_t>::min(), + kVersionUnlimited = std::numeric_limits<int32_t>::max(), + kVersionO_MR1 = 27, + kVersionP = 28, +}; + // Class to encapsulate the signature of a member (ArtField or ArtMethod). This // is used as a helper when matching prefixes, and when logging the signature. class MemberSignature { @@ -146,59 +155,31 @@ class MemberSignature { void WarnAboutAccess(AccessMethod access_method, ApiList list); - void LogAccessToEventLog(AccessMethod access_method, Action action_taken); + void LogAccessToEventLog(AccessMethod access_method, bool access_denied); + + // Calls back into managed code to notify VMRuntime.nonSdkApiUsageConsumer that + // |member| was accessed. This is usually called when an API is on the black, + // dark grey or light grey lists. Given that the callback can execute arbitrary + // code, a call to this method can result in thread suspension. + void NotifyHiddenApiListener(AccessMethod access_method); }; template<typename T> -Action GetMemberActionImpl(T* member, - ApiList api_list, - Action action, - AccessMethod access_method) +bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_); -// 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 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; - } - - if (!caller_dex_cache.IsNull()) { - const DexFile* caller_dex_file = caller_dex_cache->GetDexFile(); - 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 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. +// Returns true if access to `member` should be denied in the given context. +// The decision is based on whether the caller is in a trusted context or not. +// Because determining the access context can be expensive, a lambda function +// "fn_get_access_context" is lazily invoked after other criteria have been +// considered. // 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_is_trusted, - AccessMethod access_method) +inline bool ShouldDenyAccessToMember(T* member, + std::function<AccessContext()> fn_get_access_context, + AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(member != nullptr); @@ -210,53 +191,34 @@ inline Action GetMemberAction(T* member, // results, e.g. print whitelist warnings (b/78327881). ApiList api_list = member->GetHiddenApiAccessFlags(); - Action action = GetActionFromAccessFlags(member->GetHiddenApiAccessFlags()); - if (action == kAllow) { - // Nothing to do. - return action; + // Exit early if member is on the whitelist. + if (api_list == ApiList::kWhitelist) { + return false; } - // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access. + // Check if caller is exempted from access checks. // This can be *very* expensive. Save it for last. - if (fn_caller_is_trusted(self)) { - // Caller is trusted. Exit. - return kAllow; + if (fn_get_access_context().IsTrusted()) { + return false; } - // Member is hidden and caller is not in the platform. - return detail::GetMemberActionImpl(member, api_list, action, access_method); -} - -inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller) REQUIRES_SHARED(Locks::mutator_lock_) { - return !caller.IsNull() && - detail::IsCallerTrusted(caller, caller->GetClassLoader(), caller->GetDexCache()); + // Member is hidden and caller is not exempted. Enter slow path. + return detail::ShouldDenyAccessToMemberImpl(member, api_list, access_method); } -// Returns true if access to `member` should be denied to a caller loaded with -// `caller_class_loader`. -// This function might print warnings into the log if the member is hidden. +// Helper method for callers where access context can be determined beforehand. +// Wraps AccessContext in a lambda and passes it to the real ShouldDenyAccessToMember. template<typename T> -inline Action GetMemberAction(T* member, - ObjPtr<mirror::ClassLoader> caller_class_loader, - ObjPtr<mirror::DexCache> caller_dex_cache, - AccessMethod access_method) +inline bool ShouldDenyAccessToMember(T* member, + AccessContext access_context, + AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { - bool is_caller_trusted = - detail::IsCallerTrusted(/* caller= */ nullptr, caller_class_loader, caller_dex_cache); - return GetMemberAction(member, - /* thread= */ nullptr, - [is_caller_trusted] (Thread*) { return is_caller_trusted; }, - access_method); + return ShouldDenyAccessToMember( + member, + [&] () REQUIRES_SHARED(Locks::mutator_lock_) { return access_context; }, + access_method); } -// Calls back into managed code to notify VMRuntime.nonSdkApiUsageConsumer that -// |member| was accessed. This is usually called when an API is on the black, -// dark grey or light grey lists. Given that the callback can execute arbitrary -// code, a call to this method can result in thread suspension. -template<typename T> void NotifyHiddenApiListener(T* member) - REQUIRES_SHARED(Locks::mutator_lock_); - - } // namespace hiddenapi } // namespace art |