1 /*
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 #include "hidden_api_finder.h"
18
19 #include "dex/class_accessor-inl.h"
20 #include "dex/code_item_accessors-inl.h"
21 #include "dex/dex_instruction-inl.h"
22 #include "dex/dex_file.h"
23 #include "dex/method_reference.h"
24 #include "hidden_api.h"
25 #include "resolver.h"
26 #include "veridex.h"
27
28 #include <iostream>
29
30 namespace art {
31
CheckMethod(uint32_t method_id,VeridexResolver * resolver,MethodReference ref)32 void HiddenApiFinder::CheckMethod(uint32_t method_id,
33 VeridexResolver* resolver,
34 MethodReference ref) {
35 // Note: we always query whether a method is in boot, as the app
36 // might define blacklisted APIs (which won't be used at runtime).
37 const auto& name = HiddenApi::GetApiMethodName(resolver->GetDexFile(), method_id);
38 method_locations_[name].push_back(ref);
39 }
40
CheckField(uint32_t field_id,VeridexResolver * resolver,MethodReference ref)41 void HiddenApiFinder::CheckField(uint32_t field_id,
42 VeridexResolver* resolver,
43 MethodReference ref) {
44 // Note: we always query whether a field is in a boot, as the app
45 // might define blacklisted APIs (which won't be used at runtime).
46 const auto& name = HiddenApi::GetApiFieldName(resolver->GetDexFile(), field_id);
47 field_locations_[name].push_back(ref);
48 }
49
CollectAccesses(VeridexResolver * resolver,const ClassFilter & class_filter)50 void HiddenApiFinder::CollectAccesses(VeridexResolver* resolver,
51 const ClassFilter& class_filter) {
52 const DexFile& dex_file = resolver->GetDexFile();
53 // Look at all types referenced in this dex file. Any of these
54 // types can lead to being used through reflection.
55 for (uint32_t i = 0; i < dex_file.NumTypeIds(); ++i) {
56 std::string name(dex_file.StringByTypeIdx(dex::TypeIndex(i)));
57 classes_.insert(name);
58 }
59 // Note: we collect strings constants only referenced in code items as the string table
60 // contains other kind of strings (eg types).
61 for (ClassAccessor accessor : dex_file.GetClasses()) {
62 if (class_filter.Matches(accessor.GetDescriptor())) {
63 for (const ClassAccessor::Method& method : accessor.GetMethods()) {
64 for (const DexInstructionPcPair& inst : method.GetInstructions()) {
65 switch (inst->Opcode()) {
66 case Instruction::CONST_STRING: {
67 dex::StringIndex string_index(inst->VRegB_21c());
68 const auto& name = std::string(dex_file.StringDataByIdx(string_index));
69 // Cheap filtering on the string literal. We know it cannot be a field/method/class
70 // if it contains a space.
71 if (name.find(' ') == std::string::npos) {
72 // Class names at the Java level are of the form x.y.z, but the list encodes
73 // them of the form Lx/y/z;. Inner classes have '$' for both Java level class
74 // names in strings, and hidden API lists.
75 std::string str = HiddenApi::ToInternalName(name);
76 // Note: we can query the lists directly, as HiddenApi added classes that own
77 // private methods and fields in them.
78 // We don't add class names to the `strings_` set as we know method/field names
79 // don't have '.' or '/'. All hidden API class names have a '/'.
80 if (hidden_api_.IsInBoot(str)) {
81 classes_.insert(str);
82 } else if (hidden_api_.IsInBoot(name)) {
83 // Could be something passed to JNI.
84 classes_.insert(name);
85 } else {
86 // We only keep track of the location for strings, as these will be the
87 // field/method names the user is interested in.
88 strings_.insert(name);
89 reflection_locations_[name].push_back(method.GetReference());
90 }
91 }
92 break;
93 }
94 case Instruction::INVOKE_DIRECT:
95 case Instruction::INVOKE_INTERFACE:
96 case Instruction::INVOKE_STATIC:
97 case Instruction::INVOKE_SUPER:
98 case Instruction::INVOKE_VIRTUAL: {
99 CheckMethod(inst->VRegB_35c(), resolver, method.GetReference());
100 break;
101 }
102
103 case Instruction::INVOKE_DIRECT_RANGE:
104 case Instruction::INVOKE_INTERFACE_RANGE:
105 case Instruction::INVOKE_STATIC_RANGE:
106 case Instruction::INVOKE_SUPER_RANGE:
107 case Instruction::INVOKE_VIRTUAL_RANGE: {
108 CheckMethod(inst->VRegB_3rc(), resolver, method.GetReference());
109 break;
110 }
111
112 case Instruction::IGET:
113 case Instruction::IGET_WIDE:
114 case Instruction::IGET_OBJECT:
115 case Instruction::IGET_BOOLEAN:
116 case Instruction::IGET_BYTE:
117 case Instruction::IGET_CHAR:
118 case Instruction::IGET_SHORT: {
119 CheckField(inst->VRegC_22c(), resolver, method.GetReference());
120 break;
121 }
122
123 case Instruction::IPUT:
124 case Instruction::IPUT_WIDE:
125 case Instruction::IPUT_OBJECT:
126 case Instruction::IPUT_BOOLEAN:
127 case Instruction::IPUT_BYTE:
128 case Instruction::IPUT_CHAR:
129 case Instruction::IPUT_SHORT: {
130 CheckField(inst->VRegC_22c(), resolver, method.GetReference());
131 break;
132 }
133
134 case Instruction::SGET:
135 case Instruction::SGET_WIDE:
136 case Instruction::SGET_OBJECT:
137 case Instruction::SGET_BOOLEAN:
138 case Instruction::SGET_BYTE:
139 case Instruction::SGET_CHAR:
140 case Instruction::SGET_SHORT: {
141 CheckField(inst->VRegB_21c(), resolver, method.GetReference());
142 break;
143 }
144
145 case Instruction::SPUT:
146 case Instruction::SPUT_WIDE:
147 case Instruction::SPUT_OBJECT:
148 case Instruction::SPUT_BOOLEAN:
149 case Instruction::SPUT_BYTE:
150 case Instruction::SPUT_CHAR:
151 case Instruction::SPUT_SHORT: {
152 CheckField(inst->VRegB_21c(), resolver, method.GetReference());
153 break;
154 }
155
156 default:
157 break;
158 }
159 }
160 }
161 }
162 }
163 }
164
Run(const std::vector<std::unique_ptr<VeridexResolver>> & resolvers,const ClassFilter & class_filter)165 void HiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers,
166 const ClassFilter& class_filter) {
167 for (const std::unique_ptr<VeridexResolver>& resolver : resolvers) {
168 CollectAccesses(resolver.get(), class_filter);
169 }
170 }
171
Dump(std::ostream & os,HiddenApiStats * stats,bool dump_reflection)172 void HiddenApiFinder::Dump(std::ostream& os,
173 HiddenApiStats* stats,
174 bool dump_reflection) {
175 // Dump methods from hidden APIs linked against.
176 for (const std::pair<const std::string,
177 std::vector<MethodReference>>& pair : method_locations_) {
178 const auto& name = pair.first;
179 if (hidden_api_.GetSignatureSource(name) != SignatureSource::APP &&
180 hidden_api_.ShouldReport(name)) {
181 stats->linking_count++;
182 hiddenapi::ApiList api_list = hidden_api_.GetApiList(pair.first);
183 stats->api_counts[api_list.GetIntValue()]++;
184 os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):";
185 os << std::endl;
186 HiddenApiFinder::DumpReferences(os, pair.second);
187 os << std::endl;
188 }
189 }
190
191 // Dump fields from hidden APIs linked against.
192 for (const std::pair<const std::string,
193 std::vector<MethodReference>>& pair : field_locations_) {
194 const auto& name = pair.first;
195 if (hidden_api_.GetSignatureSource(name) != SignatureSource::APP &&
196 hidden_api_.ShouldReport(name)) {
197 stats->linking_count++;
198 hiddenapi::ApiList api_list = hidden_api_.GetApiList(pair.first);
199 stats->api_counts[api_list.GetIntValue()]++;
200 // Note: There is a test depending on this output format,
201 // so please be careful when you modify the format. b/123662832
202 os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):";
203 os << std::endl;
204 HiddenApiFinder::DumpReferences(os, pair.second);
205 os << std::endl;
206 }
207 }
208
209 if (dump_reflection) {
210 // Dump potential reflection uses.
211 for (const std::string& cls : classes_) {
212 for (const std::string& name : strings_) {
213 std::string full_name = cls + "->" + name;
214 if (hidden_api_.GetSignatureSource(full_name) != SignatureSource::APP &&
215 hidden_api_.ShouldReport(full_name)) {
216 hiddenapi::ApiList api_list = hidden_api_.GetApiList(full_name);
217 stats->api_counts[api_list.GetIntValue()]++;
218 stats->reflection_count++;
219 // Note: There is a test depending on this output format,
220 // so please be careful when you modify the format. b/123662832
221 os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name
222 << " potential use(s):";
223 os << std::endl;
224 HiddenApiFinder::DumpReferences(os, reflection_locations_[name]);
225 os << std::endl;
226 }
227 }
228 }
229 }
230 }
231
DumpReferences(std::ostream & os,const std::vector<MethodReference> & references)232 void HiddenApiFinder::DumpReferences(std::ostream& os,
233 const std::vector<MethodReference>& references) {
234 static const char* kPrefix = " ";
235
236 // Count number of occurrences of each reference, to make the output clearer.
237 std::map<std::string, size_t> counts;
238 for (const MethodReference& ref : references) {
239 std::string ref_string = HiddenApi::GetApiMethodName(ref);
240 if (!counts.count(ref_string)) {
241 counts[ref_string] = 0;
242 }
243 counts[ref_string]++;
244 }
245
246 for (const std::pair<const std::string, size_t>& pair : counts) {
247 os << kPrefix << pair.first;
248 if (pair.second > 1) {
249 os << " (" << pair.second << " occurrences)";
250 }
251 os << std::endl;
252 }
253 }
254
255 } // namespace art
256