Merge "[veridex] Detect reflection uses." into pi-dev
diff --git a/libdexfile/dex/hidden_api_access_flags.h b/libdexfile/dex/hidden_api_access_flags.h
index 441b3c1..b62d044 100644
--- a/libdexfile/dex/hidden_api_access_flags.h
+++ b/libdexfile/dex/hidden_api_access_flags.h
@@ -18,6 +18,7 @@
 #define ART_LIBDEXFILE_DEX_HIDDEN_API_ACCESS_FLAGS_H_
 
 #include "base/bit_utils.h"
+#include "base/macros.h"
 #include "dex/modifiers.h"
 
 namespace art {
diff --git a/tools/veridex/Android.bp b/tools/veridex/Android.bp
index a74bf3d..cbf62d9 100644
--- a/tools/veridex/Android.bp
+++ b/tools/veridex/Android.bp
@@ -17,6 +17,7 @@
     host_supported: true,
     srcs: [
         "hidden_api.cc",
+        "hidden_api_finder.cc",
         "resolver.cc",
         "veridex.cc",
     ],
diff --git a/tools/veridex/hidden_api.cc b/tools/veridex/hidden_api.cc
index 33e499b..93f921a 100644
--- a/tools/veridex/hidden_api.cc
+++ b/tools/veridex/hidden_api.cc
@@ -44,17 +44,6 @@
   return ss.str();
 }
 
-bool HiddenApi::LogIfIn(const std::string& name,
-                        const std::set<std::string>& list,
-                        const std::string& log,
-                        const std::string& access_kind) {
-  if (list.find(name) != list.end()) {
-    LOG(WARNING) << std::string(log) << " usage found " << name << " (" << access_kind << ")";
-    return true;
-  }
-  return false;
-}
-
 void HiddenApi::FillList(const char* filename, std::set<std::string>& entries) {
   if (filename == nullptr) {
     return;
diff --git a/tools/veridex/hidden_api.h b/tools/veridex/hidden_api.h
index 282e7cf..5893b8a 100644
--- a/tools/veridex/hidden_api.h
+++ b/tools/veridex/hidden_api.h
@@ -17,6 +17,9 @@
 #ifndef ART_TOOLS_VERIDEX_HIDDEN_API_H_
 #define ART_TOOLS_VERIDEX_HIDDEN_API_H_
 
+#include "dex/hidden_api_access_flags.h"
+
+#include <ostream>
 #include <set>
 #include <string>
 
@@ -35,10 +38,20 @@
     FillList(blacklist, blacklist_);
   }
 
-  bool LogIfInList(const std::string& name, const char* access_kind) const {
-    return LogIfIn(name, blacklist_, "Blacklist", access_kind) ||
-        LogIfIn(name, dark_greylist_, "Dark greylist", access_kind) ||
-        LogIfIn(name, light_greylist_, "Light greylist", access_kind);
+  HiddenApiAccessFlags::ApiList GetApiList(const std::string& name) const {
+    if (IsInList(name, blacklist_)) {
+      return HiddenApiAccessFlags::kBlacklist;
+    } else if (IsInList(name, dark_greylist_)) {
+      return HiddenApiAccessFlags::kDarkGreylist;
+    } else if (IsInList(name, light_greylist_)) {
+      return HiddenApiAccessFlags::kLightGreylist;
+    } else {
+      return HiddenApiAccessFlags::kWhitelist;
+    }
+  }
+
+  bool IsInRestrictionList(const std::string& name) const {
+    return GetApiList(name) != HiddenApiAccessFlags::kWhitelist;
   }
 
   static std::string GetApiMethodName(const DexFile& dex_file, uint32_t method_index);
@@ -46,10 +59,9 @@
   static std::string GetApiFieldName(const DexFile& dex_file, uint32_t field_index);
 
  private:
-  static bool LogIfIn(const std::string& name,
-                      const std::set<std::string>& list,
-                      const std::string& log,
-                      const std::string& access_kind);
+  static bool IsInList(const std::string& name, const std::set<std::string>& list) {
+    return list.find(name) != list.end();
+  }
 
   static void FillList(const char* filename, std::set<std::string>& entries);
 
diff --git a/tools/veridex/hidden_api_finder.cc b/tools/veridex/hidden_api_finder.cc
new file mode 100644
index 0000000..d611f78
--- /dev/null
+++ b/tools/veridex/hidden_api_finder.cc
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hidden_api_finder.h"
+
+#include "dex/code_item_accessors-inl.h"
+#include "dex/dex_instruction-inl.h"
+#include "dex/dex_file.h"
+#include "dex/method_reference.h"
+#include "hidden_api.h"
+#include "resolver.h"
+#include "veridex.h"
+
+#include <iostream>
+
+namespace art {
+
+void HiddenApiFinder::CheckMethod(uint32_t method_id,
+                                  VeridexResolver* resolver,
+                                  MethodReference ref) {
+  // Cheap check that the method is resolved. If it is, we know it's not in
+  // a restricted list.
+  if (resolver->GetMethod(method_id) != nullptr) {
+    return;
+  }
+  std::string name = HiddenApi::GetApiMethodName(resolver->GetDexFile(), method_id);
+  if (hidden_api_.IsInRestrictionList(name)) {
+    method_locations_[name].push_back(ref);
+  }
+}
+
+void HiddenApiFinder::CheckField(uint32_t field_id,
+                                 VeridexResolver* resolver,
+                                 MethodReference ref) {
+  // Cheap check that the field is resolved. If it is, we know it's not in
+  // a restricted list.
+  if (resolver->GetField(field_id) != nullptr) {
+    return;
+  }
+  std::string name = HiddenApi::GetApiFieldName(resolver->GetDexFile(), field_id);
+  if (hidden_api_.IsInRestrictionList(name)) {
+    field_locations_[name].push_back(ref);
+  }
+}
+
+void HiddenApiFinder::CollectAccesses(VeridexResolver* resolver) {
+  const DexFile& dex_file = resolver->GetDexFile();
+  size_t class_def_count = dex_file.NumClassDefs();
+  for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) {
+    const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
+    const uint8_t* class_data = dex_file.GetClassData(class_def);
+    if (class_data == nullptr) {
+      // Empty class.
+      continue;
+    }
+    ClassDataItemIterator it(dex_file, class_data);
+    it.SkipAllFields();
+    for (; it.HasNextMethod(); it.Next()) {
+      const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
+      if (code_item == nullptr) {
+        continue;
+      }
+      CodeItemDataAccessor code_item_accessor(dex_file, code_item);
+      for (const DexInstructionPcPair& inst : code_item_accessor) {
+        switch (inst->Opcode()) {
+          case Instruction::CONST_CLASS: {
+            dex::TypeIndex type_index(inst->VRegB_21c());
+            std::string name = dex_file.StringByTypeIdx(type_index);
+            // Only keep classes that are in a restriction list.
+            if (hidden_api_.IsInRestrictionList(name)) {
+              classes_.insert(name);
+            }
+            break;
+          }
+          case Instruction::CONST_STRING: {
+            dex::StringIndex string_index(inst->VRegB_21c());
+            std::string name = std::string(dex_file.StringDataByIdx(string_index));
+            // Cheap filtering on the string literal. We know it cannot be a field/method/class
+            // if it contains a space.
+            if (name.find(' ') == std::string::npos) {
+              // Class names at the Java level are of the form x.y.z, but the list encodes
+              // them of the form Lx/y/z;. Inner classes have '$' for both Java level class
+              // names in strings, and hidden API lists.
+              std::string str = name;
+              std::replace(str.begin(), str.end(), '.', '/');
+              str = "L" + str + ";";
+              // Note: we can query the lists directly, as HiddenApi added classes that own
+              // private methods and fields in them.
+              // We don't add class names to the `strings_` set as we know method/field names
+              // don't have '.' or '/'. All hidden API class names have a '/'.
+              if (hidden_api_.IsInRestrictionList(str)) {
+                classes_.insert(str);
+              } else if (hidden_api_.IsInRestrictionList(name)) {
+                // Could be something passed to JNI.
+                classes_.insert(name);
+              } else {
+                // We only keep track of the location for strings, as these will be the
+                // field/method names the user is interested in.
+                strings_.insert(name);
+                reflection_locations_[name].push_back(
+                    MethodReference(&dex_file, it.GetMemberIndex()));
+              }
+            }
+            break;
+          }
+          case Instruction::INVOKE_DIRECT:
+          case Instruction::INVOKE_INTERFACE:
+          case Instruction::INVOKE_STATIC:
+          case Instruction::INVOKE_SUPER:
+          case Instruction::INVOKE_VIRTUAL: {
+            CheckMethod(
+                inst->VRegB_35c(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+            break;
+          }
+
+          case Instruction::INVOKE_DIRECT_RANGE:
+          case Instruction::INVOKE_INTERFACE_RANGE:
+          case Instruction::INVOKE_STATIC_RANGE:
+          case Instruction::INVOKE_SUPER_RANGE:
+          case Instruction::INVOKE_VIRTUAL_RANGE: {
+            CheckMethod(
+                inst->VRegB_3rc(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+            break;
+          }
+
+          case Instruction::IGET:
+          case Instruction::IGET_WIDE:
+          case Instruction::IGET_OBJECT:
+          case Instruction::IGET_BOOLEAN:
+          case Instruction::IGET_BYTE:
+          case Instruction::IGET_CHAR:
+          case Instruction::IGET_SHORT: {
+            CheckField(
+                inst->VRegC_22c(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+            break;
+          }
+
+          case Instruction::IPUT:
+          case Instruction::IPUT_WIDE:
+          case Instruction::IPUT_OBJECT:
+          case Instruction::IPUT_BOOLEAN:
+          case Instruction::IPUT_BYTE:
+          case Instruction::IPUT_CHAR:
+          case Instruction::IPUT_SHORT: {
+            CheckField(
+                inst->VRegC_22c(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+            break;
+          }
+
+          case Instruction::SGET:
+          case Instruction::SGET_WIDE:
+          case Instruction::SGET_OBJECT:
+          case Instruction::SGET_BOOLEAN:
+          case Instruction::SGET_BYTE:
+          case Instruction::SGET_CHAR:
+          case Instruction::SGET_SHORT: {
+            CheckField(
+                inst->VRegB_21c(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+            break;
+          }
+
+          case Instruction::SPUT:
+          case Instruction::SPUT_WIDE:
+          case Instruction::SPUT_OBJECT:
+          case Instruction::SPUT_BOOLEAN:
+          case Instruction::SPUT_BYTE:
+          case Instruction::SPUT_CHAR:
+          case Instruction::SPUT_SHORT: {
+            CheckField(
+                inst->VRegB_21c(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+            break;
+          }
+
+          default:
+            break;
+        }
+      }
+    }
+  }
+}
+
+static std::string GetApiMethodName(MethodReference ref) {
+  return HiddenApi::GetApiMethodName(*ref.dex_file, ref.index);
+}
+
+void HiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers) {
+  for (const std::unique_ptr<VeridexResolver>& resolver : resolvers) {
+    CollectAccesses(resolver.get());
+  }
+
+  Dump(std::cout);
+}
+
+void HiddenApiFinder::Dump(std::ostream& os) {
+  static const char* kPrefix = "       ";
+  uint32_t count = 0;
+  uint32_t linking_count = method_locations_.size() + field_locations_.size();
+  uint32_t api_counts[4] = {0, 0, 0, 0};
+
+  // Dump methods from hidden APIs linked against.
+  for (const std::pair<std::string, std::vector<MethodReference>>& pair : method_locations_) {
+    HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(pair.first);
+    api_counts[api_list]++;
+    os << "#" << ++count << ": Linking " << api_list << " " << pair.first << " use(s):";
+    os << std::endl;
+    for (const MethodReference& ref : pair.second) {
+      os << kPrefix << GetApiMethodName(ref) << std::endl;
+    }
+    os << std::endl;
+  }
+
+  // Dump fields from hidden APIs linked against.
+  for (const std::pair<std::string, std::vector<MethodReference>>& pair : field_locations_) {
+    HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(pair.first);
+    api_counts[api_list]++;
+    os << "#" << ++count << ": Linking " << api_list << " " << pair.first << " use(s):";
+    os << std::endl;
+    for (const MethodReference& ref : pair.second) {
+      os << kPrefix << GetApiMethodName(ref) << std::endl;
+    }
+    os << std::endl;
+  }
+
+  // Dump potential reflection uses.
+  for (const std::string& cls : classes_) {
+    for (const std::string& name : strings_) {
+      std::string full_name = cls + "->" + name;
+      HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name);
+      api_counts[api_list]++;
+      if (api_list != HiddenApiAccessFlags::kWhitelist) {
+        os << "#" << ++count << ": Reflection " << api_list << " " << full_name
+           << " potential use(s):";
+        os << std::endl;
+        for (const MethodReference& ref : reflection_locations_[name]) {
+          os << kPrefix << GetApiMethodName(ref) << std::endl;
+        }
+        os << std::endl;
+      }
+    }
+  }
+
+  os << count << " hidden API(s) used: "
+     << linking_count << " linked against, "
+     << count - linking_count << " potentially through reflection" << std::endl;
+  os << kPrefix << api_counts[HiddenApiAccessFlags::kBlacklist]
+     << " in blacklist" << std::endl;
+  os << kPrefix << api_counts[HiddenApiAccessFlags::kDarkGreylist]
+     << " in dark greylist" << std::endl;
+  os << kPrefix << api_counts[HiddenApiAccessFlags::kLightGreylist]
+     << " in light greylist" << std::endl;
+}
+
+}  // namespace art
diff --git a/tools/veridex/hidden_api_finder.h b/tools/veridex/hidden_api_finder.h
new file mode 100644
index 0000000..243079c
--- /dev/null
+++ b/tools/veridex/hidden_api_finder.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_TOOLS_VERIDEX_HIDDEN_API_FINDER_H_
+#define ART_TOOLS_VERIDEX_HIDDEN_API_FINDER_H_
+
+#include "dex/method_reference.h"
+
+#include <iostream>
+#include <map>
+#include <set>
+#include <string>
+
+namespace art {
+
+class HiddenApi;
+class VeridexResolver;
+
+/**
+ * Reports potential uses of hidden APIs from static linking and reflection.
+ */
+class HiddenApiFinder {
+ public:
+  explicit HiddenApiFinder(const HiddenApi& hidden_api) : hidden_api_(hidden_api) {}
+
+  // Iterate over the dex files associated with the passed resolvers to report
+  // hidden API uses.
+  void Run(const std::vector<std::unique_ptr<VeridexResolver>>& app_resolvers);
+
+ private:
+  void CollectAccesses(VeridexResolver* resolver);
+  void CheckMethod(uint32_t method_idx, VeridexResolver* resolver, MethodReference ref);
+  void CheckField(uint32_t field_idx, VeridexResolver* resolver, MethodReference ref);
+  void Dump(std::ostream& os);
+
+  const HiddenApi& hidden_api_;
+  std::set<std::string> classes_;
+  std::set<std::string> strings_;
+  std::map<std::string, std::vector<MethodReference>> reflection_locations_;
+  std::map<std::string, std::vector<MethodReference>> method_locations_;
+  std::map<std::string, std::vector<MethodReference>> field_locations_;
+};
+
+}  // namespace art
+
+#endif  // ART_TOOLS_VERIDEX_HIDDEN_API_FINDER_H_
diff --git a/tools/veridex/resolver.cc b/tools/veridex/resolver.cc
index 6ab872e..13dda5c 100644
--- a/tools/veridex/resolver.cc
+++ b/tools/veridex/resolver.cc
@@ -277,10 +277,8 @@
   return field_info;
 }
 
-void VeridexResolver::ResolveAll(const HiddenApi& hidden_api) {
+void VeridexResolver::ResolveAll() {
   for (uint32_t i = 0; i < dex_file_.NumTypeIds(); ++i) {
-    // Note: we don't look at HiddenApi for types, as the lists don't contain
-    // classes.
     if (GetVeriClass(dex::TypeIndex(i)) == nullptr) {
       LOG(WARNING) << "Unresolved " << dex_file_.PrettyType(dex::TypeIndex(i));
     }
@@ -288,17 +286,13 @@
 
   for (uint32_t i = 0; i < dex_file_.NumMethodIds(); ++i) {
     if (GetMethod(i) == nullptr) {
-      if (!hidden_api.LogIfInList(HiddenApi::GetApiMethodName(dex_file_, i), "Linking")) {
-        LOG(WARNING) << "Unresolved: " << dex_file_.PrettyMethod(i);
-      }
+      LOG(WARNING) << "Unresolved: " << dex_file_.PrettyMethod(i);
     }
   }
 
   for (uint32_t i = 0; i < dex_file_.NumFieldIds(); ++i) {
     if (GetField(i) == nullptr) {
-      if (!hidden_api.LogIfInList(HiddenApi::GetApiFieldName(dex_file_, i), "Linking")) {
-        LOG(WARNING) << "Unresolved: " << dex_file_.PrettyField(i);
-      }
+      LOG(WARNING) << "Unresolved: " << dex_file_.PrettyField(i);
     }
   }
 }
diff --git a/tools/veridex/resolver.h b/tools/veridex/resolver.h
index 82f6aae..06c8aa7 100644
--- a/tools/veridex/resolver.h
+++ b/tools/veridex/resolver.h
@@ -66,9 +66,13 @@
                           const char* field_name,
                           const char* field_type);
 
-  // Resolve all type_id/method_id/field_id. Log for unresolved
-  // entities, or entities part of a hidden API list.
-  void ResolveAll(const HiddenApi& hidden_api);
+  // Resolve all type_id/method_id/field_id.
+  void ResolveAll();
+
+  // The dex file this resolver is associated to.
+  const DexFile& GetDexFile() const {
+    return dex_file_;
+  }
 
  private:
   // Return the resolver where `kls` is from.
diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc
index c5203fe..16e9f0e 100644
--- a/tools/veridex/veridex.cc
+++ b/tools/veridex/veridex.cc
@@ -21,6 +21,7 @@
 #include "dex/dex_file.h"
 #include "dex/dex_file_loader.h"
 #include "hidden_api.h"
+#include "hidden_api_finder.h"
 #include "resolver.h"
 
 #include <sstream>
@@ -162,11 +163,10 @@
     std::vector<std::unique_ptr<VeridexResolver>> app_resolvers;
     Resolve(app_dex_files, resolver_map, type_map, &app_resolvers);
 
-    // Resolve all type_id/method_id/field_id of app dex files.
+    // Find and log uses of hidden APIs.
     HiddenApi hidden_api(options.blacklist, options.dark_greylist, options.light_greylist);
-    for (const std::unique_ptr<VeridexResolver>& resolver : app_resolvers) {
-      resolver->ResolveAll(hidden_api);
-    }
+    HiddenApiFinder api_finder(hidden_api);
+    api_finder.Run(app_resolvers);
 
     return 0;
   }