blob: bbedda017e351871378a9bc881cb490160f9090f [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"
Mathew Inwood7d74ef52018-03-16 14:18:33 +000022#include "base/dumpable.h"
David Sehr67bf42e2018-02-26 16:43:04 -080023#include "dex/hidden_api_access_flags.h"
David Brazdil8ce3bfa2018-03-12 18:01:18 +000024#include "mirror/class-inl.h"
David Brazdil5a61bb72018-01-19 16:59:46 +000025#include "reflection.h"
26#include "runtime.h"
27
28namespace art {
29namespace hiddenapi {
30
Mathew Inwood597d7f62018-03-22 11:36:47 +000031// Hidden API enforcement policy
32// This must be kept in sync with ApplicationInfo.ApiEnforcementPolicy in
33// frameworks/base/core/java/android/content/pm/ApplicationInfo.java
34enum class EnforcementPolicy {
35 kNoChecks = 0,
36 kAllLists = 1, // ban anything but whitelist
37 kDarkGreyAndBlackList = 2, // ban dark grey & blacklist
38 kBlacklistOnly = 3, // ban blacklist violations only
39 kMax = kBlacklistOnly,
40};
41
42inline EnforcementPolicy EnforcementPolicyFromInt(int api_policy_int) {
43 DCHECK_GE(api_policy_int, 0);
44 DCHECK_LE(api_policy_int, static_cast<int>(EnforcementPolicy::kMax));
45 return static_cast<EnforcementPolicy>(api_policy_int);
46}
47
David Brazdila02cb112018-01-31 11:36:39 +000048enum Action {
49 kAllow,
50 kAllowButWarn,
David Brazdil92265222018-02-02 11:21:40 +000051 kAllowButWarnAndToast,
David Brazdila02cb112018-01-31 11:36:39 +000052 kDeny
53};
David Brazdil5a61bb72018-01-19 16:59:46 +000054
David Brazdil068d68d2018-02-12 13:04:17 -080055enum AccessMethod {
56 kReflection,
David Brazdil8ce3bfa2018-03-12 18:01:18 +000057 kJNI,
58 kLinking,
David Brazdil068d68d2018-02-12 13:04:17 -080059};
60
61inline std::ostream& operator<<(std::ostream& os, AccessMethod value) {
62 switch (value) {
63 case kReflection:
64 os << "reflection";
65 break;
66 case kJNI:
67 os << "JNI";
68 break;
David Brazdil8ce3bfa2018-03-12 18:01:18 +000069 case kLinking:
70 os << "linking";
71 break;
David Brazdil068d68d2018-02-12 13:04:17 -080072 }
73 return os;
74}
75
Mathew Inwood597d7f62018-03-22 11:36:47 +000076static constexpr bool EnumsEqual(EnforcementPolicy policy, HiddenApiAccessFlags::ApiList apiList) {
77 return static_cast<int>(policy) == static_cast<int>(apiList);
78}
79
David Brazdila02cb112018-01-31 11:36:39 +000080inline Action GetMemberAction(uint32_t access_flags) {
Mathew Inwood597d7f62018-03-22 11:36:47 +000081 EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
82 if (policy == EnforcementPolicy::kNoChecks) {
83 // Exit early. Nothing to enforce.
84 return kAllow;
85 }
86
87 HiddenApiAccessFlags::ApiList api_list = HiddenApiAccessFlags::DecodeFromRuntime(access_flags);
88 if (api_list == HiddenApiAccessFlags::kWhitelist) {
89 return kAllow;
90 }
91 // The logic below relies on equality of values in the enums EnforcementPolicy and
92 // HiddenApiAccessFlags::ApiList, and their ordering. Assert that this is as expected.
93 static_assert(
94 EnumsEqual(EnforcementPolicy::kAllLists, HiddenApiAccessFlags::kLightGreylist) &&
95 EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) &&
96 EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist),
97 "Mismatch between EnforcementPolicy and ApiList enums");
98 static_assert(
99 EnforcementPolicy::kAllLists < EnforcementPolicy::kDarkGreyAndBlackList &&
100 EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly,
101 "EnforcementPolicy values ordering not correct");
102 if (static_cast<int>(policy) > static_cast<int>(api_list)) {
103 return api_list == HiddenApiAccessFlags::kDarkGreylist
104 ? kAllowButWarnAndToast
105 : kAllowButWarn;
106 } else {
107 return kDeny;
David Brazdil5a61bb72018-01-19 16:59:46 +0000108 }
109}
110
David Brazdilee7d2fd2018-01-20 17:25:23 +0000111
Mathew Inwood7d74ef52018-03-16 14:18:33 +0000112// Class to encapsulate the signature of a member (ArtField or ArtMethod). This
113// is used as a helper when matching prefixes, and when logging the signature.
114class MemberSignature {
115 private:
116 std::string member_type_;
117 std::vector<std::string> signature_parts_;
118 std::string tmp_;
119
120 public:
121 explicit MemberSignature(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) {
122 member_type_ = "field";
123 signature_parts_ = {
124 field->GetDeclaringClass()->GetDescriptor(&tmp_),
125 "->",
126 field->GetName(),
127 ":",
128 field->GetTypeDescriptor()
129 };
130 }
131
132 explicit MemberSignature(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
133 member_type_ = "method";
134 signature_parts_ = {
135 method->GetDeclaringClass()->GetDescriptor(&tmp_),
136 "->",
137 method->GetName(),
138 method->GetSignature().ToString()
139 };
140 }
141
142 const std::vector<std::string>& Parts() const {
143 return signature_parts_;
144 }
145
146 void Dump(std::ostream& os) const {
147 for (std::string part : signature_parts_) {
148 os << part;
149 }
150 }
151 // Performs prefix match on this member. Since the full member signature is
152 // composed of several parts, we match each part in turn (rather than
153 // building the entire thing in memory and performing a simple prefix match)
154 bool DoesPrefixMatch(const std::string& prefix) const {
155 size_t pos = 0;
156 for (const std::string& part : signature_parts_) {
157 size_t count = std::min(prefix.length() - pos, part.length());
158 if (prefix.compare(pos, count, part, 0, count) == 0) {
159 pos += count;
160 } else {
161 return false;
162 }
163 }
164 // We have a complete match if all parts match (we exit the loop without
165 // returning) AND we've matched the whole prefix.
166 return pos == prefix.length();
167 }
168
169 bool IsExempted(const std::vector<std::string>& exemptions) {
170 for (const std::string& exemption : exemptions) {
171 if (DoesPrefixMatch(exemption)) {
172 return true;
173 }
174 }
175 return false;
176 }
177
178 void WarnAboutAccess(AccessMethod access_method, HiddenApiAccessFlags::ApiList list) {
179 LOG(WARNING) << "Accessing hidden " << member_type_ << " " << Dumpable<MemberSignature>(*this)
180 << " (" << list << ", " << access_method << ")";
181 }
182};
David Brazdilee7d2fd2018-01-20 17:25:23 +0000183
David Brazdila02cb112018-01-31 11:36:39 +0000184// Returns true if access to `member` should be denied to the caller of the
185// reflective query. The decision is based on whether the caller is in boot
186// class path or not. Because different users of this function determine this
187// in a different way, `fn_caller_in_boot(self)` is called and should return
188// true if the caller is in boot class path.
David Brazdil8ce3bfa2018-03-12 18:01:18 +0000189// This function might print warnings into the log if the member is hidden.
David Brazdilee7d2fd2018-01-20 17:25:23 +0000190template<typename T>
David Brazdila02cb112018-01-31 11:36:39 +0000191inline bool ShouldBlockAccessToMember(T* member,
192 Thread* self,
David Brazdil068d68d2018-02-12 13:04:17 -0800193 std::function<bool(Thread*)> fn_caller_in_boot,
194 AccessMethod access_method)
David Brazdilee7d2fd2018-01-20 17:25:23 +0000195 REQUIRES_SHARED(Locks::mutator_lock_) {
196 DCHECK(member != nullptr);
David Brazdilee7d2fd2018-01-20 17:25:23 +0000197
David Brazdila02cb112018-01-31 11:36:39 +0000198 Action action = GetMemberAction(member->GetAccessFlags());
199 if (action == kAllow) {
200 // Nothing to do.
201 return false;
202 }
203
204 // Member is hidden. Walk the stack to find the caller.
205 // This can be *very* expensive. Save it for last.
206 if (fn_caller_in_boot(self)) {
207 // Caller in boot class path. Exit.
208 return false;
209 }
210
David Brazdil068d68d2018-02-12 13:04:17 -0800211 // Member is hidden and we are not in the boot class path.
212
Mathew Inwood7d74ef52018-03-16 14:18:33 +0000213 // Get the signature, we need it later.
214 MemberSignature member_signature(member);
215
216 Runtime* runtime = Runtime::Current();
217
218 if (action == kDeny) {
219 // If we were about to deny, check for an exemption first.
220 // Exempted APIs are treated as light grey list.
221 if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
222 action = kAllowButWarn;
223 // Avoid re-examining the exemption list next time.
224 // Note this results in the warning below showing "light greylist", which
225 // seems like what one would expect. Exemptions effectively add new members to
226 // the light greylist.
227 member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
228 member->GetAccessFlags(), HiddenApiAccessFlags::kLightGreylist));
229 }
230 }
231
David Brazdil068d68d2018-02-12 13:04:17 -0800232 // Print a log message with information about this class member access.
233 // We do this regardless of whether we block the access or not.
Mathew Inwood7d74ef52018-03-16 14:18:33 +0000234 member_signature.WarnAboutAccess(access_method,
235 HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags()));
David Brazdil068d68d2018-02-12 13:04:17 -0800236
David Brazdil92265222018-02-02 11:21:40 +0000237 if (action == kDeny) {
Mathew Inwood597d7f62018-03-22 11:36:47 +0000238 // Block access
David Brazdil92265222018-02-02 11:21:40 +0000239 return true;
David Brazdila02cb112018-01-31 11:36:39 +0000240 }
David Brazdil068d68d2018-02-12 13:04:17 -0800241
242 // Allow access to this member but print a warning.
243 DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast);
244
245 // Depending on a runtime flag, we might move the member into whitelist and
246 // skip the warning the next time the member is accessed.
247 if (runtime->ShouldDedupeHiddenApiWarnings()) {
248 member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
249 member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
250 }
251
252 // If this action requires a UI warning, set the appropriate flag.
253 if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) {
Mathew Inwood597d7f62018-03-22 11:36:47 +0000254 runtime->SetPendingHiddenApiWarning(true);
David Brazdil068d68d2018-02-12 13:04:17 -0800255 }
256
257 return false;
David Brazdilee7d2fd2018-01-20 17:25:23 +0000258}
259
David Brazdil8ce3bfa2018-03-12 18:01:18 +0000260// Returns true if access to `member` should be denied to a caller loaded with
261// `caller_class_loader`.
262// This function might print warnings into the log if the member is hidden.
263template<typename T>
264inline bool ShouldBlockAccessToMember(T* member,
265 ObjPtr<mirror::ClassLoader> caller_class_loader,
266 AccessMethod access_method)
David Brazdilee7d2fd2018-01-20 17:25:23 +0000267 REQUIRES_SHARED(Locks::mutator_lock_) {
David Brazdil8ce3bfa2018-03-12 18:01:18 +0000268 bool caller_in_boot = (caller_class_loader.IsNull());
269 return ShouldBlockAccessToMember(member,
270 /* thread */ nullptr,
271 [caller_in_boot] (Thread*) { return caller_in_boot; },
272 access_method);
David Brazdilee7d2fd2018-01-20 17:25:23 +0000273}
274
David Brazdil5a61bb72018-01-19 16:59:46 +0000275} // namespace hiddenapi
276} // namespace art
277
278#endif // ART_RUNTIME_HIDDEN_API_H_