diff options
-rw-r--r-- | runtime/hidden_api.h | 69 | ||||
-rw-r--r-- | runtime/native/dalvik_system_ZygoteHooks.cc | 49 | ||||
-rw-r--r-- | runtime/native/java_lang_Class.cc | 4 | ||||
-rw-r--r-- | runtime/runtime.cc | 13 | ||||
-rw-r--r-- | runtime/runtime.h | 14 | ||||
-rw-r--r-- | runtime/well_known_classes.cc | 9 | ||||
-rw-r--r-- | test/674-hiddenapi/hiddenapi.cc | 3 |
7 files changed, 107 insertions, 54 deletions
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index f2ea2fdaaa..321d55d9b7 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -27,6 +27,23 @@ namespace art { namespace hiddenapi { +// Hidden API enforcement policy +// This must be kept in sync with ApplicationInfo.ApiEnforcementPolicy in +// frameworks/base/core/java/android/content/pm/ApplicationInfo.java +enum class EnforcementPolicy { + kNoChecks = 0, + kAllLists = 1, // ban anything but whitelist + kDarkGreyAndBlackList = 2, // ban dark grey & blacklist + kBlacklistOnly = 3, // ban blacklist violations only + kMax = kBlacklistOnly, +}; + +inline EnforcementPolicy EnforcementPolicyFromInt(int api_policy_int) { + DCHECK_GE(api_policy_int, 0); + DCHECK_LE(api_policy_int, static_cast<int>(EnforcementPolicy::kMax)); + return static_cast<EnforcementPolicy>(api_policy_int); +} + enum Action { kAllow, kAllowButWarn, @@ -59,16 +76,38 @@ inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { return os; } +static constexpr bool EnumsEqual(EnforcementPolicy policy, HiddenApiAccessFlags::ApiList apiList) { + return static_cast<int>(policy) == static_cast<int>(apiList); +} + inline Action GetMemberAction(uint32_t access_flags) { - switch (HiddenApiAccessFlags::DecodeFromRuntime(access_flags)) { - case HiddenApiAccessFlags::kWhitelist: - return kAllow; - case HiddenApiAccessFlags::kLightGreylist: - return kAllowButWarn; - case HiddenApiAccessFlags::kDarkGreylist: - return kAllowButWarnAndToast; - case HiddenApiAccessFlags::kBlacklist: - return kDeny; + EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); + if (policy == EnforcementPolicy::kNoChecks) { + // Exit early. Nothing to enforce. + return kAllow; + } + + HiddenApiAccessFlags::ApiList api_list = HiddenApiAccessFlags::DecodeFromRuntime(access_flags); + if (api_list == HiddenApiAccessFlags::kWhitelist) { + return kAllow; + } + // The logic below relies on equality of values in the enums EnforcementPolicy and + // HiddenApiAccessFlags::ApiList, and their ordering. Assert that this is as expected. + static_assert( + EnumsEqual(EnforcementPolicy::kAllLists, HiddenApiAccessFlags::kLightGreylist) && + EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) && + EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist), + "Mismatch between EnforcementPolicy and ApiList enums"); + static_assert( + EnforcementPolicy::kAllLists < EnforcementPolicy::kDarkGreyAndBlackList && + EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly, + "EnforcementPolicy values ordering not correct"); + if (static_cast<int>(policy) > static_cast<int>(api_list)) { + return api_list == HiddenApiAccessFlags::kDarkGreylist + ? kAllowButWarnAndToast + : kAllowButWarn; + } else { + return kDeny; } } @@ -107,12 +146,6 @@ inline bool ShouldBlockAccessToMember(T* member, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(member != nullptr); - Runtime* runtime = Runtime::Current(); - - if (!runtime->AreHiddenApiChecksEnabled()) { - // Exit early. Nothing to enforce. - return false; - } Action action = GetMemberAction(member->GetAccessFlags()); if (action == kAllow) { @@ -133,14 +166,16 @@ inline bool ShouldBlockAccessToMember(T* member, // We do this regardless of whether we block the access or not. WarnAboutMemberAccess(member, access_method); - // Block access if on blacklist. if (action == kDeny) { + // Block access return true; } // Allow access to this member but print a warning. DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast); + Runtime* runtime = Runtime::Current(); + // Depending on a runtime flag, we might move the member into whitelist and // skip the warning the next time the member is accessed. if (runtime->ShouldDedupeHiddenApiWarnings()) { @@ -150,7 +185,7 @@ inline bool ShouldBlockAccessToMember(T* member, // If this action requires a UI warning, set the appropriate flag. if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) { - Runtime::Current()->SetPendingHiddenApiWarning(true); + runtime->SetPendingHiddenApiWarning(true); } return false; diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index 89135698e3..cbc2aeb41f 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -162,19 +162,24 @@ static void CollectNonDebuggableClasses() REQUIRES(!Locks::mutator_lock_) { // Must match values in com.android.internal.os.Zygote. enum { - DEBUG_ENABLE_JDWP = 1, - DEBUG_ENABLE_CHECKJNI = 1 << 1, - DEBUG_ENABLE_ASSERT = 1 << 2, - DEBUG_ENABLE_SAFEMODE = 1 << 3, - DEBUG_ENABLE_JNI_LOGGING = 1 << 4, - DEBUG_GENERATE_DEBUG_INFO = 1 << 5, - DEBUG_ALWAYS_JIT = 1 << 6, - DEBUG_NATIVE_DEBUGGABLE = 1 << 7, - DEBUG_JAVA_DEBUGGABLE = 1 << 8, - DISABLE_VERIFIER = 1 << 9, - ONLY_USE_SYSTEM_OAT_FILES = 1 << 10, - ENABLE_HIDDEN_API_CHECKS = 1 << 11, - DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12, + DEBUG_ENABLE_JDWP = 1, + DEBUG_ENABLE_CHECKJNI = 1 << 1, + DEBUG_ENABLE_ASSERT = 1 << 2, + DEBUG_ENABLE_SAFEMODE = 1 << 3, + DEBUG_ENABLE_JNI_LOGGING = 1 << 4, + DEBUG_GENERATE_DEBUG_INFO = 1 << 5, + DEBUG_ALWAYS_JIT = 1 << 6, + DEBUG_NATIVE_DEBUGGABLE = 1 << 7, + DEBUG_JAVA_DEBUGGABLE = 1 << 8, + DISABLE_VERIFIER = 1 << 9, + ONLY_USE_SYSTEM_OAT_FILES = 1 << 10, + DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 11, + HIDDEN_API_ENFORCEMENT_POLICY_MASK = (1 << 12) + | (1 << 13), + + // bits to shift (flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) by to get a value + // corresponding to hiddenapi::EnforcementPolicy + API_ENFORCEMENT_POLICY_SHIFT = CTZ(HIDDEN_API_ENFORCEMENT_POLICY_MASK), }; static uint32_t EnableDebugFeatures(uint32_t runtime_flags) { @@ -285,7 +290,8 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, // Our system thread ID, etc, has changed so reset Thread state. thread->InitAfterFork(); runtime_flags = EnableDebugFeatures(runtime_flags); - bool do_hidden_api_checks = false; + hiddenapi::EnforcementPolicy api_enforcement_policy = hiddenapi::EnforcementPolicy::kNoChecks; + bool dedupe_hidden_api_warnings = true; if ((runtime_flags & DISABLE_VERIFIER) != 0) { Runtime::Current()->DisableVerifier(); @@ -297,10 +303,9 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, runtime_flags &= ~ONLY_USE_SYSTEM_OAT_FILES; } - if ((runtime_flags & ENABLE_HIDDEN_API_CHECKS) != 0) { - do_hidden_api_checks = true; - runtime_flags &= ~ENABLE_HIDDEN_API_CHECKS; - } + api_enforcement_policy = hiddenapi::EnforcementPolicyFromInt( + (runtime_flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) >> API_ENFORCEMENT_POLICY_SHIFT); + runtime_flags &= ~HIDDEN_API_ENFORCEMENT_POLICY_MASK; if (runtime_flags != 0) { LOG(ERROR) << StringPrintf("Unknown bits set in runtime_flags: %#x", runtime_flags); @@ -351,11 +356,13 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, } } + bool do_hidden_api_checks = api_enforcement_policy != hiddenapi::EnforcementPolicy::kNoChecks; DCHECK(!(is_system_server && do_hidden_api_checks)) - << "SystemServer should be forked with ENABLE_HIDDEN_API_CHECKS"; + << "SystemServer should be forked with EnforcementPolicy::kDisable"; DCHECK(!(is_zygote && do_hidden_api_checks)) - << "Child zygote processes should be forked with ENABLE_HIDDEN_API_CHECKS"; - Runtime::Current()->SetHiddenApiChecksEnabled(do_hidden_api_checks); + << "Child zygote processes should be forked with EnforcementPolicy::kDisable"; + Runtime::Current()->SetHiddenApiEnforcementPolicy(api_enforcement_policy); + Runtime::Current()->SetDedupeHiddenApiWarnings(dedupe_hidden_api_warnings); // Clear the hidden API warning flag, in case it was set. Runtime::Current()->SetPendingHiddenApiWarning(false); diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 25d50376de..fc61c9597e 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -89,8 +89,8 @@ static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator // access hidden APIs. This can be *very* expensive. Never call this in a loop. ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - return Runtime::Current()->AreHiddenApiChecksEnabled() && - !IsCallerInBootClassPath(self); + hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); + return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerInBootClassPath(self); } // Returns true if the first non-ClassClass caller up the stack should not be diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 7d9d3426fc..53982ae833 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -267,7 +267,7 @@ Runtime::Runtime() oat_file_manager_(nullptr), is_low_memory_mode_(false), safe_mode_(false), - do_hidden_api_checks_(false), + hidden_api_policy_(hiddenapi::EnforcementPolicy::kNoChecks), pending_hidden_api_warning_(false), dedupe_hidden_api_warnings_(true), always_set_hidden_api_warning_flag_(false), @@ -1196,9 +1196,14 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // by default and we only enable them if: // (a) runtime was started with a flag that enables the checks, or // (b) Zygote forked a new process that is not exempt (see ZygoteHooks). - do_hidden_api_checks_ = runtime_options.Exists(Opt::HiddenApiChecks); - DCHECK(!is_zygote_ || !do_hidden_api_checks_) - << "Zygote should not be started with hidden API checks"; + bool do_hidden_api_checks = runtime_options.Exists(Opt::HiddenApiChecks); + DCHECK(!is_zygote_ || !do_hidden_api_checks); + // TODO pass the actual enforcement policy in, rather than just a single bit. + // As is, we're encoding some logic here about which specific policy to use, which would be better + // controlled by the framework. + hidden_api_policy_ = do_hidden_api_checks + ? hiddenapi::EnforcementPolicy::kBlacklistOnly + : hiddenapi::EnforcementPolicy::kNoChecks; no_sig_chain_ = runtime_options.Exists(Opt::NoSigChain); force_native_bridge_ = runtime_options.Exists(Opt::ForceNativeBridge); diff --git a/runtime/runtime.h b/runtime/runtime.h index c7f650ea3f..dba31b2939 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -49,6 +49,10 @@ class AbstractSystemWeakHolder; class Heap; } // namespace gc +namespace hiddenapi { +enum class EnforcementPolicy; +} // namespace hiddenapi + namespace jit { class Jit; class JitOptions; @@ -520,12 +524,12 @@ class Runtime { bool IsVerificationEnabled() const; bool IsVerificationSoftFail() const; - void SetHiddenApiChecksEnabled(bool value) { - do_hidden_api_checks_ = value; + void SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy policy) { + hidden_api_policy_ = policy; } - bool AreHiddenApiChecksEnabled() const { - return do_hidden_api_checks_; + hiddenapi::EnforcementPolicy GetHiddenApiEnforcementPolicy() const { + return hidden_api_policy_; } void SetPendingHiddenApiWarning(bool value) { @@ -990,7 +994,7 @@ class Runtime { bool safe_mode_; // Whether access checks on hidden API should be performed. - bool do_hidden_api_checks_; + hiddenapi::EnforcementPolicy hidden_api_policy_; // Whether the application has used an API which is not restricted but we // should issue a warning about it. diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 67ea64be74..bf36ccf0fa 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -24,6 +24,7 @@ #include <android-base/stringprintf.h> #include "entrypoints/quick/quick_entrypoints_enum.h" +#include "hidden_api.h" #include "jni_internal.h" #include "mirror/class.h" #include "mirror/throwable.h" @@ -287,17 +288,17 @@ class ScopedHiddenApiExemption { public: explicit ScopedHiddenApiExemption(Runtime* runtime) : runtime_(runtime), - initially_enabled_(runtime_->AreHiddenApiChecksEnabled()) { - runtime_->SetHiddenApiChecksEnabled(false); + initial_policy_(runtime_->GetHiddenApiEnforcementPolicy()) { + runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks); } ~ScopedHiddenApiExemption() { - runtime_->SetHiddenApiChecksEnabled(initially_enabled_); + runtime_->SetHiddenApiEnforcementPolicy(initial_policy_); } private: Runtime* runtime_; - const bool initially_enabled_; + const hiddenapi::EnforcementPolicy initial_policy_; DISALLOW_COPY_AND_ASSIGN(ScopedHiddenApiExemption); }; diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc index effa37ade4..04c3fbf03a 100644 --- a/test/674-hiddenapi/hiddenapi.cc +++ b/test/674-hiddenapi/hiddenapi.cc @@ -16,6 +16,7 @@ #include "class_linker.h" #include "dex/art_dex_file_loader.h" +#include "hidden_api.h" #include "jni.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" @@ -27,7 +28,7 @@ namespace Test674HiddenApi { extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) { Runtime* runtime = Runtime::Current(); - runtime->SetHiddenApiChecksEnabled(true); + runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly); runtime->SetDedupeHiddenApiWarnings(false); runtime->AlwaysSetHiddenApiWarningFlag(); } |