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