diff options
| -rw-r--r-- | libdexfile/dex/hidden_api_access_flags.h | 1 | ||||
| -rw-r--r-- | tools/veridex/Android.bp | 1 | ||||
| -rw-r--r-- | tools/veridex/hidden_api.cc | 11 | ||||
| -rw-r--r-- | tools/veridex/hidden_api.h | 28 | ||||
| -rw-r--r-- | tools/veridex/hidden_api_finder.cc | 266 | ||||
| -rw-r--r-- | tools/veridex/hidden_api_finder.h | 59 | ||||
| -rw-r--r-- | tools/veridex/resolver.cc | 12 | ||||
| -rw-r--r-- | tools/veridex/resolver.h | 10 | ||||
| -rw-r--r-- | tools/veridex/veridex.cc | 8 |
9 files changed, 361 insertions, 35 deletions
diff --git a/libdexfile/dex/hidden_api_access_flags.h b/libdexfile/dex/hidden_api_access_flags.h index 441b3c14b5..b62d044c6a 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 a74bf3d7f9..cbf62d9e9c 100644 --- a/tools/veridex/Android.bp +++ b/tools/veridex/Android.bp @@ -17,6 +17,7 @@ cc_binary { 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 33e499bfc3..93f921a25f 100644 --- a/tools/veridex/hidden_api.cc +++ b/tools/veridex/hidden_api.cc @@ -44,17 +44,6 @@ std::string HiddenApi::GetApiFieldName(const DexFile& dex_file, uint32_t field_i 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 282e7cf8e8..5893b8ae33 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 @@ class HiddenApi { 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 @@ class HiddenApi { 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 0000000000..d611f78eed --- /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 0000000000..243079c187 --- /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 6ab872ed6f..13dda5c199 100644 --- a/tools/veridex/resolver.cc +++ b/tools/veridex/resolver.cc @@ -277,10 +277,8 @@ VeriField VeridexResolver::GetField(uint32_t field_index) { 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 @@ void VeridexResolver::ResolveAll(const HiddenApi& hidden_api) { 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 82f6aaeddd..06c8aa70c5 100644 --- a/tools/veridex/resolver.h +++ b/tools/veridex/resolver.h @@ -66,9 +66,13 @@ class VeridexResolver { 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 c5203fea66..16e9f0e55b 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 @@ class Veridex { 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; } |