blob: dbe776e050154ccf0bad10dabf4d054dcec3f178 [file] [log] [blame]
David Brazdil5a61bb72018-01-19 16:59:46 +00001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef ART_RUNTIME_HIDDEN_API_H_
18#define ART_RUNTIME_HIDDEN_API_H_
19
David Brazdil8ce3bfa2018-03-12 18:01:18 +000020#include "art_field-inl.h"
21#include "art_method-inl.h"
David Sehr67bf42e2018-02-26 16:43:04 -080022#include "dex/hidden_api_access_flags.h"
David Brazdil8ce3bfa2018-03-12 18:01:18 +000023#include "mirror/class-inl.h"
David Brazdil5a61bb72018-01-19 16:59:46 +000024#include "reflection.h"
25#include "runtime.h"
26
27namespace art {
28namespace hiddenapi {
29
Mathew Inwood597d7f62018-03-22 11:36:47 +000030// Hidden API enforcement policy
31// This must be kept in sync with ApplicationInfo.ApiEnforcementPolicy in
32// frameworks/base/core/java/android/content/pm/ApplicationInfo.java
33enum class EnforcementPolicy {
34 kNoChecks = 0,
35 kAllLists = 1, // ban anything but whitelist
36 kDarkGreyAndBlackList = 2, // ban dark grey & blacklist
37 kBlacklistOnly = 3, // ban blacklist violations only
38 kMax = kBlacklistOnly,
39};
40
41inline EnforcementPolicy EnforcementPolicyFromInt(int api_policy_int) {
42 DCHECK_GE(api_policy_int, 0);
43 DCHECK_LE(api_policy_int, static_cast<int>(EnforcementPolicy::kMax));
44 return static_cast<EnforcementPolicy>(api_policy_int);
45}
46
David Brazdila02cb112018-01-31 11:36:39 +000047enum Action {
48 kAllow,
49 kAllowButWarn,
David Brazdil92265222018-02-02 11:21:40 +000050 kAllowButWarnAndToast,
David Brazdila02cb112018-01-31 11:36:39 +000051 kDeny
52};
David Brazdil5a61bb72018-01-19 16:59:46 +000053
David Brazdil068d68d2018-02-12 13:04:17 -080054enum AccessMethod {
55 kReflection,
David Brazdil8ce3bfa2018-03-12 18:01:18 +000056 kJNI,
57 kLinking,
David Brazdil068d68d2018-02-12 13:04:17 -080058};
59
60inline std::ostream& operator<<(std::ostream& os, AccessMethod value) {
61 switch (value) {
62 case kReflection:
63 os << "reflection";
64 break;
65 case kJNI:
66 os << "JNI";
67 break;
David Brazdil8ce3bfa2018-03-12 18:01:18 +000068 case kLinking:
69 os << "linking";
70 break;
David Brazdil068d68d2018-02-12 13:04:17 -080071 }
72 return os;
73}
74
Mathew Inwood597d7f62018-03-22 11:36:47 +000075static constexpr bool EnumsEqual(EnforcementPolicy policy, HiddenApiAccessFlags::ApiList apiList) {
76 return static_cast<int>(policy) == static_cast<int>(apiList);
77}
78
David Brazdila02cb112018-01-31 11:36:39 +000079inline Action GetMemberAction(uint32_t access_flags) {
Mathew Inwood597d7f62018-03-22 11:36:47 +000080 EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
81 if (policy == EnforcementPolicy::kNoChecks) {
82 // Exit early. Nothing to enforce.
83 return kAllow;
84 }
85
86 HiddenApiAccessFlags::ApiList api_list = HiddenApiAccessFlags::DecodeFromRuntime(access_flags);
87 if (api_list == HiddenApiAccessFlags::kWhitelist) {
88 return kAllow;
89 }
90 // The logic below relies on equality of values in the enums EnforcementPolicy and
91 // HiddenApiAccessFlags::ApiList, and their ordering. Assert that this is as expected.
92 static_assert(
93 EnumsEqual(EnforcementPolicy::kAllLists, HiddenApiAccessFlags::kLightGreylist) &&
94 EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) &&
95 EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist),
96 "Mismatch between EnforcementPolicy and ApiList enums");
97 static_assert(
98 EnforcementPolicy::kAllLists < EnforcementPolicy::kDarkGreyAndBlackList &&
99 EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly,
100 "EnforcementPolicy values ordering not correct");
101 if (static_cast<int>(policy) > static_cast<int>(api_list)) {
102 return api_list == HiddenApiAccessFlags::kDarkGreylist
103 ? kAllowButWarnAndToast
104 : kAllowButWarn;
105 } else {
106 return kDeny;
David Brazdil5a61bb72018-01-19 16:59:46 +0000107 }
108}
109
David Brazdilee7d2fd2018-01-20 17:25:23 +0000110// Issue a warning about field access.
David Brazdil068d68d2018-02-12 13:04:17 -0800111inline void WarnAboutMemberAccess(ArtField* field, AccessMethod access_method)
112 REQUIRES_SHARED(Locks::mutator_lock_) {
David Brazdil1077bbc2018-01-31 14:33:08 +0000113 std::string tmp;
114 LOG(WARNING) << "Accessing hidden field "
115 << field->GetDeclaringClass()->GetDescriptor(&tmp) << "->"
David Brazdil068d68d2018-02-12 13:04:17 -0800116 << field->GetName() << ":" << field->GetTypeDescriptor()
117 << " (" << HiddenApiAccessFlags::DecodeFromRuntime(field->GetAccessFlags())
118 << ", " << access_method << ")";
David Brazdilee7d2fd2018-01-20 17:25:23 +0000119}
120
121// Issue a warning about method access.
David Brazdil068d68d2018-02-12 13:04:17 -0800122inline void WarnAboutMemberAccess(ArtMethod* method, AccessMethod access_method)
123 REQUIRES_SHARED(Locks::mutator_lock_) {
David Brazdil1077bbc2018-01-31 14:33:08 +0000124 std::string tmp;
125 LOG(WARNING) << "Accessing hidden method "
126 << method->GetDeclaringClass()->GetDescriptor(&tmp) << "->"
David Brazdil068d68d2018-02-12 13:04:17 -0800127 << method->GetName() << method->GetSignature().ToString()
128 << " (" << HiddenApiAccessFlags::DecodeFromRuntime(method->GetAccessFlags())
129 << ", " << access_method << ")";
David Brazdilee7d2fd2018-01-20 17:25:23 +0000130}
131
David Brazdila02cb112018-01-31 11:36:39 +0000132// Returns true if access to `member` should be denied to the caller of the
David Brazdil8e1a7cb2018-03-27 08:14:25 +0000133// reflective query. The decision is based on whether the caller is in the
134// platform or not. Because different users of this function determine this
135// in a different way, `fn_caller_in_platform(self)` is called and should
136// return true if the caller is located in the platform.
David Brazdil8ce3bfa2018-03-12 18:01:18 +0000137// This function might print warnings into the log if the member is hidden.
David Brazdilee7d2fd2018-01-20 17:25:23 +0000138template<typename T>
David Brazdila02cb112018-01-31 11:36:39 +0000139inline bool ShouldBlockAccessToMember(T* member,
140 Thread* self,
David Brazdil8e1a7cb2018-03-27 08:14:25 +0000141 std::function<bool(Thread*)> fn_caller_in_platform,
David Brazdil068d68d2018-02-12 13:04:17 -0800142 AccessMethod access_method)
David Brazdilee7d2fd2018-01-20 17:25:23 +0000143 REQUIRES_SHARED(Locks::mutator_lock_) {
144 DCHECK(member != nullptr);
David Brazdilee7d2fd2018-01-20 17:25:23 +0000145
David Brazdila02cb112018-01-31 11:36:39 +0000146 Action action = GetMemberAction(member->GetAccessFlags());
147 if (action == kAllow) {
148 // Nothing to do.
149 return false;
150 }
151
David Brazdil8e1a7cb2018-03-27 08:14:25 +0000152 // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access.
David Brazdila02cb112018-01-31 11:36:39 +0000153 // This can be *very* expensive. Save it for last.
David Brazdil8e1a7cb2018-03-27 08:14:25 +0000154 if (fn_caller_in_platform(self)) {
155 // Caller in the platform. Exit.
David Brazdila02cb112018-01-31 11:36:39 +0000156 return false;
157 }
158
David Brazdil8e1a7cb2018-03-27 08:14:25 +0000159 // Member is hidden and caller is not in the platform.
David Brazdil068d68d2018-02-12 13:04:17 -0800160
161 // Print a log message with information about this class member access.
162 // We do this regardless of whether we block the access or not.
163 WarnAboutMemberAccess(member, access_method);
164
David Brazdil92265222018-02-02 11:21:40 +0000165 if (action == kDeny) {
Mathew Inwood597d7f62018-03-22 11:36:47 +0000166 // Block access
David Brazdil92265222018-02-02 11:21:40 +0000167 return true;
David Brazdila02cb112018-01-31 11:36:39 +0000168 }
David Brazdil068d68d2018-02-12 13:04:17 -0800169
170 // Allow access to this member but print a warning.
171 DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast);
172
Mathew Inwood597d7f62018-03-22 11:36:47 +0000173 Runtime* runtime = Runtime::Current();
174
David Brazdil068d68d2018-02-12 13:04:17 -0800175 // Depending on a runtime flag, we might move the member into whitelist and
176 // skip the warning the next time the member is accessed.
177 if (runtime->ShouldDedupeHiddenApiWarnings()) {
178 member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
179 member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
180 }
181
182 // If this action requires a UI warning, set the appropriate flag.
183 if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) {
Mathew Inwood597d7f62018-03-22 11:36:47 +0000184 runtime->SetPendingHiddenApiWarning(true);
David Brazdil068d68d2018-02-12 13:04:17 -0800185 }
186
187 return false;
David Brazdilee7d2fd2018-01-20 17:25:23 +0000188}
189
David Brazdil8e1a7cb2018-03-27 08:14:25 +0000190// Returns true if the caller is either loaded by the boot strap class loader or comes from
191// a dex file located in ${ANDROID_ROOT}/framework/.
192inline bool IsCallerInPlatformDex(ObjPtr<mirror::ClassLoader> caller_class_loader,
193 ObjPtr<mirror::DexCache> caller_dex_cache)
194 REQUIRES_SHARED(Locks::mutator_lock_) {
195 if (caller_class_loader.IsNull()) {
196 return true;
197 } else if (caller_dex_cache.IsNull()) {
198 return false;
199 } else {
200 const DexFile* caller_dex_file = caller_dex_cache->GetDexFile();
201 return caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile();
202 }
203}
204
205inline bool IsCallerInPlatformDex(ObjPtr<mirror::Class> caller)
206 REQUIRES_SHARED(Locks::mutator_lock_) {
207 return !caller.IsNull() && IsCallerInPlatformDex(caller->GetClassLoader(), caller->GetDexCache());
208}
209
David Brazdil8ce3bfa2018-03-12 18:01:18 +0000210// Returns true if access to `member` should be denied to a caller loaded with
211// `caller_class_loader`.
212// This function might print warnings into the log if the member is hidden.
213template<typename T>
214inline bool ShouldBlockAccessToMember(T* member,
215 ObjPtr<mirror::ClassLoader> caller_class_loader,
David Brazdil8e1a7cb2018-03-27 08:14:25 +0000216 ObjPtr<mirror::DexCache> caller_dex_cache,
David Brazdil8ce3bfa2018-03-12 18:01:18 +0000217 AccessMethod access_method)
David Brazdilee7d2fd2018-01-20 17:25:23 +0000218 REQUIRES_SHARED(Locks::mutator_lock_) {
David Brazdil8e1a7cb2018-03-27 08:14:25 +0000219 bool caller_in_platform = IsCallerInPlatformDex(caller_class_loader, caller_dex_cache);
David Brazdil8ce3bfa2018-03-12 18:01:18 +0000220 return ShouldBlockAccessToMember(member,
221 /* thread */ nullptr,
David Brazdil8e1a7cb2018-03-27 08:14:25 +0000222 [caller_in_platform] (Thread*) { return caller_in_platform; },
David Brazdil8ce3bfa2018-03-12 18:01:18 +0000223 access_method);
David Brazdilee7d2fd2018-01-20 17:25:23 +0000224}
225
David Brazdil5a61bb72018-01-19 16:59:46 +0000226} // namespace hiddenapi
227} // namespace art
228
229#endif // ART_RUNTIME_HIDDEN_API_H_