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