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 blocked 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 blocked 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 CodeItemInstructionAccessor codes = method.GetInstructions();
65 const uint32_t max_pc = codes.InsnsSizeInCodeUnits();
66 for (const DexInstructionPcPair& inst : codes) {
67 if (inst.DexPc() >= max_pc) {
68 // We need to prevent abnormal access for outside of code
69 break;
70 }
71
72 switch (inst->Opcode()) {
73 case Instruction::CONST_STRING: {
74 dex::StringIndex string_index(inst->VRegB_21c());
75 const auto& name = std::string(dex_file.StringDataByIdx(string_index));
76 // Cheap filtering on the string literal. We know it cannot be a field/method/class
77 // if it contains a space.
78 if (name.find(' ') == std::string::npos) {
79 // Class names at the Java level are of the form x.y.z, but the list encodes
80 // them of the form Lx/y/z;. Inner classes have '$' for both Java level class
81 // names in strings, and hidden API lists.
82 std::string str = HiddenApi::ToInternalName(name);
83 // Note: we can query the lists directly, as HiddenApi added classes that own
84 // private methods and fields in them.
85 // We don't add class names to the `strings_` set as we know method/field names
86 // don't have '.' or '/'. All hidden API class names have a '/'.
87 if (hidden_api_.IsInBoot(str)) {
88 classes_.insert(str);
89 } else if (hidden_api_.IsInBoot(name)) {
90 // Could be something passed to JNI.
91 classes_.insert(name);
92 } else {
93 // We only keep track of the location for strings, as these will be the
94 // field/method names the user is interested in.
95 strings_.insert(name);
96 reflection_locations_[name].push_back(method.GetReference());
97 }
98 }
99 break;
100 }
101 case Instruction::INVOKE_DIRECT:
102 case Instruction::INVOKE_INTERFACE:
103 case Instruction::INVOKE_STATIC:
104 case Instruction::INVOKE_SUPER:
105 case Instruction::INVOKE_VIRTUAL: {
106 CheckMethod(inst->VRegB_35c(), resolver, method.GetReference());
107 break;
108 }
109
110 case Instruction::INVOKE_DIRECT_RANGE:
111 case Instruction::INVOKE_INTERFACE_RANGE:
112 case Instruction::INVOKE_STATIC_RANGE:
113 case Instruction::INVOKE_SUPER_RANGE:
114 case Instruction::INVOKE_VIRTUAL_RANGE: {
115 CheckMethod(inst->VRegB_3rc(), resolver, method.GetReference());
116 break;
117 }
118
119 case Instruction::IGET:
120 case Instruction::IGET_WIDE:
121 case Instruction::IGET_OBJECT:
122 case Instruction::IGET_BOOLEAN:
123 case Instruction::IGET_BYTE:
124 case Instruction::IGET_CHAR:
125 case Instruction::IGET_SHORT: {
126 CheckField(inst->VRegC_22c(), resolver, method.GetReference());
127 break;
128 }
129
130 case Instruction::IPUT:
131 case Instruction::IPUT_WIDE:
132 case Instruction::IPUT_OBJECT:
133 case Instruction::IPUT_BOOLEAN:
134 case Instruction::IPUT_BYTE:
135 case Instruction::IPUT_CHAR:
136 case Instruction::IPUT_SHORT: {
137 CheckField(inst->VRegC_22c(), resolver, method.GetReference());
138 break;
139 }
140
141 case Instruction::SGET:
142 case Instruction::SGET_WIDE:
143 case Instruction::SGET_OBJECT:
144 case Instruction::SGET_BOOLEAN:
145 case Instruction::SGET_BYTE:
146 case Instruction::SGET_CHAR:
147 case Instruction::SGET_SHORT: {
148 CheckField(inst->VRegB_21c(), resolver, method.GetReference());
149 break;
150 }
151
152 case Instruction::SPUT:
153 case Instruction::SPUT_WIDE:
154 case Instruction::SPUT_OBJECT:
155 case Instruction::SPUT_BOOLEAN:
156 case Instruction::SPUT_BYTE:
157 case Instruction::SPUT_CHAR:
158 case Instruction::SPUT_SHORT: {
159 CheckField(inst->VRegB_21c(), resolver, method.GetReference());
160 break;
161 }
162
163 default:
164 break;
165 }
166 }
167 }
168 }
169 }
170 }
171
Run(const std::vector<std::unique_ptr<VeridexResolver>> & resolvers,const ClassFilter & class_filter)172 void HiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers,
173 const ClassFilter& class_filter) {
174 for (const std::unique_ptr<VeridexResolver>& resolver : resolvers) {
175 CollectAccesses(resolver.get(), class_filter);
176 }
177 }
178
Dump(std::ostream & os,HiddenApiStats * stats,bool dump_reflection)179 void HiddenApiFinder::Dump(std::ostream& os,
180 HiddenApiStats* stats,
181 bool dump_reflection) {
182 // Dump methods from hidden APIs linked against.
183 for (const std::pair<const std::string,
184 std::vector<MethodReference>>& pair : method_locations_) {
185 const auto& name = pair.first;
186 if (hidden_api_.GetSignatureSource(name) != SignatureSource::APP &&
187 hidden_api_.ShouldReport(name)) {
188 stats->linking_count++;
189 hiddenapi::ApiList api_list = hidden_api_.GetApiList(pair.first);
190 stats->api_counts[api_list.GetIntValue()]++;
191 os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):";
192 os << std::endl;
193 HiddenApiFinder::DumpReferences(os, pair.second);
194 os << std::endl;
195 }
196 }
197
198 // Dump fields from hidden APIs linked against.
199 for (const std::pair<const std::string,
200 std::vector<MethodReference>>& pair : field_locations_) {
201 const auto& name = pair.first;
202 if (hidden_api_.GetSignatureSource(name) != SignatureSource::APP &&
203 hidden_api_.ShouldReport(name)) {
204 stats->linking_count++;
205 hiddenapi::ApiList api_list = hidden_api_.GetApiList(pair.first);
206 stats->api_counts[api_list.GetIntValue()]++;
207 // Note: There is a test depending on this output format,
208 // so please be careful when you modify the format. b/123662832
209 os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):";
210 os << std::endl;
211 HiddenApiFinder::DumpReferences(os, pair.second);
212 os << std::endl;
213 }
214 }
215
216 if (dump_reflection) {
217 // Dump potential reflection uses.
218 for (const std::string& cls : classes_) {
219 for (const std::string& name : strings_) {
220 std::string full_name = cls + "->" + name;
221 if (hidden_api_.GetSignatureSource(full_name) != SignatureSource::APP &&
222 hidden_api_.ShouldReport(full_name)) {
223 hiddenapi::ApiList api_list = hidden_api_.GetApiList(full_name);
224 stats->api_counts[api_list.GetIntValue()]++;
225 stats->reflection_count++;
226 // Note: There is a test depending on this output format,
227 // so please be careful when you modify the format. b/123662832
228 os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name
229 << " potential use(s):";
230 os << std::endl;
231 HiddenApiFinder::DumpReferences(os, reflection_locations_[name]);
232 os << std::endl;
233 }
234 }
235 }
236 }
237 }
238
DumpReferences(std::ostream & os,const std::vector<MethodReference> & references)239 void HiddenApiFinder::DumpReferences(std::ostream& os,
240 const std::vector<MethodReference>& references) {
241 static const char* kPrefix = " ";
242
243 // Count number of occurrences of each reference, to make the output clearer.
244 std::map<std::string, size_t> counts;
245 for (const MethodReference& ref : references) {
246 std::string ref_string = HiddenApi::GetApiMethodName(ref);
247 if (!counts.count(ref_string)) {
248 counts[ref_string] = 0;
249 }
250 counts[ref_string]++;
251 }
252
253 for (const std::pair<const std::string, size_t>& pair : counts) {
254 os << kPrefix << pair.first;
255 if (pair.second > 1) {
256 os << " (" << pair.second << " occurrences)";
257 }
258 os << std::endl;
259 }
260 }
261
262 } // namespace art
263