diff options
| author | 2019-05-07 19:23:41 +0100 | |
|---|---|---|
| committer | 2019-05-08 09:03:53 +0000 | |
| commit | d76e1238fddfd6eed608d97929c333e921e4826f (patch) | |
| tree | d5d081944ddd6786f1b9194aa1aac83c5244cc27 | |
| parent | 1fe5839d10114209a75d1716bd81c353e4096810 (diff) | |
veridex: Add cmd line flag to filter app classes by name
Veridex can now skip analyzing classes which do not match a provided
filter. The filter is passed to veridex using '--app-class-filter='
command line flag as a comma-separated list of class descriptor
prefixes, e.g. 'Landroid/net/,Landroid/utils/' matches all classes in
the 'android.net' and 'android.utils' packages and their subpackages.
Test: compiles, manually test against APKs
Change-Id: I4db9c3625399ee313a1ba0aba7f3d9073570cc70
| -rw-r--r-- | tools/veridex/class_filter.h | 48 | ||||
| -rw-r--r-- | tools/veridex/hidden_api_finder.cc | 184 | ||||
| -rw-r--r-- | tools/veridex/hidden_api_finder.h | 6 | ||||
| -rw-r--r-- | tools/veridex/precise_hidden_api_finder.cc | 18 | ||||
| -rw-r--r-- | tools/veridex/precise_hidden_api_finder.h | 5 | ||||
| -rw-r--r-- | tools/veridex/veridex.cc | 14 |
6 files changed, 175 insertions, 100 deletions
diff --git a/tools/veridex/class_filter.h b/tools/veridex/class_filter.h new file mode 100644 index 0000000000..aa74d53732 --- /dev/null +++ b/tools/veridex/class_filter.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2019 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_CLASS_FILTER_H_ +#define ART_TOOLS_VERIDEX_CLASS_FILTER_H_ + +#include <android-base/strings.h> + +namespace art { + +class ClassFilter { + public: + explicit ClassFilter(const std::vector<std::string>& prefixes) : prefixes_(prefixes) {} + + bool Matches(const char* class_descriptor) const { + if (prefixes_.empty()) { + return true; + } + + for (const std::string& filter : prefixes_) { + if (android::base::StartsWith(class_descriptor, filter)) { + return true; + } + } + + return false; + } + + private: + const std::vector<std::string>& prefixes_; +}; + +} // namespace art + +#endif // ART_TOOLS_VERIDEX_CLASS_FILTER_H_ diff --git a/tools/veridex/hidden_api_finder.cc b/tools/veridex/hidden_api_finder.cc index fe6d88aa3e..3ab911e8d3 100644 --- a/tools/veridex/hidden_api_finder.cc +++ b/tools/veridex/hidden_api_finder.cc @@ -51,7 +51,8 @@ void HiddenApiFinder::CheckField(uint32_t field_id, } } -void HiddenApiFinder::CollectAccesses(VeridexResolver* resolver) { +void HiddenApiFinder::CollectAccesses(VeridexResolver* resolver, + const ClassFilter& class_filter) { const DexFile& dex_file = resolver->GetDexFile(); // Look at all types referenced in this dex file. Any of these // types can lead to being used through reflection. @@ -64,110 +65,113 @@ void HiddenApiFinder::CollectAccesses(VeridexResolver* resolver) { // Note: we collect strings constants only referenced in code items as the string table // contains other kind of strings (eg types). for (ClassAccessor accessor : dex_file.GetClasses()) { - for (const ClassAccessor::Method& method : accessor.GetMethods()) { - for (const DexInstructionPcPair& inst : method.GetInstructions()) { - switch (inst->Opcode()) { - 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 = HiddenApi::ToInternalName(name); - // 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_.IsInAnyList(str)) { - classes_.insert(str); - } else if (hidden_api_.IsInAnyList(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(method.GetReference()); + if (class_filter.Matches(accessor.GetDescriptor())) { + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + for (const DexInstructionPcPair& inst : method.GetInstructions()) { + switch (inst->Opcode()) { + 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 = HiddenApi::ToInternalName(name); + // 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_.IsInAnyList(str)) { + classes_.insert(str); + } else if (hidden_api_.IsInAnyList(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(method.GetReference()); + } } + 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, method.GetReference()); + break; } - 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, method.GetReference()); - 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, method.GetReference()); - 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, method.GetReference()); + 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, method.GetReference()); - 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, method.GetReference()); + 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, method.GetReference()); - 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, method.GetReference()); + 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, method.GetReference()); - 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, method.GetReference()); + 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, method.GetReference()); - 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, method.GetReference()); + break; + } - default: - break; + default: + break; + } } } } } } -void HiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers) { +void HiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers, + const ClassFilter& class_filter) { for (const std::unique_ptr<VeridexResolver>& resolver : resolvers) { - CollectAccesses(resolver.get()); + CollectAccesses(resolver.get(), class_filter); } } diff --git a/tools/veridex/hidden_api_finder.h b/tools/veridex/hidden_api_finder.h index 9e10c1aa67..f395e89b28 100644 --- a/tools/veridex/hidden_api_finder.h +++ b/tools/veridex/hidden_api_finder.h @@ -17,6 +17,7 @@ #ifndef ART_TOOLS_VERIDEX_HIDDEN_API_FINDER_H_ #define ART_TOOLS_VERIDEX_HIDDEN_API_FINDER_H_ +#include "class_filter.h" #include "dex/method_reference.h" #include <iostream> @@ -39,12 +40,13 @@ class HiddenApiFinder { // 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); + void Run(const std::vector<std::unique_ptr<VeridexResolver>>& app_resolvers, + const ClassFilter& app_class_filter); void Dump(std::ostream& os, HiddenApiStats* stats, bool dump_reflection); private: - void CollectAccesses(VeridexResolver* resolver); + void CollectAccesses(VeridexResolver* resolver, const ClassFilter& class_filter); void CheckMethod(uint32_t method_idx, VeridexResolver* resolver, MethodReference ref); void CheckField(uint32_t field_idx, VeridexResolver* resolver, MethodReference ref); void DumpReferences(std::ostream& os, const std::vector<MethodReference>& references); diff --git a/tools/veridex/precise_hidden_api_finder.cc b/tools/veridex/precise_hidden_api_finder.cc index be99ed29d4..7aa86dc495 100644 --- a/tools/veridex/precise_hidden_api_finder.cc +++ b/tools/veridex/precise_hidden_api_finder.cc @@ -16,6 +16,7 @@ #include "precise_hidden_api_finder.h" +#include "class_filter.h" #include "dex/class_accessor-inl.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_instruction-inl.h" @@ -32,12 +33,15 @@ namespace art { void PreciseHiddenApiFinder::RunInternal( const std::vector<std::unique_ptr<VeridexResolver>>& resolvers, + const ClassFilter& class_filter, const std::function<void(VeridexResolver*, const ClassAccessor::Method&)>& action) { for (const std::unique_ptr<VeridexResolver>& resolver : resolvers) { for (ClassAccessor accessor : resolver->GetDexFile().GetClasses()) { - for (const ClassAccessor::Method& method : accessor.GetMethods()) { - if (method.GetCodeItem() != nullptr) { - action(resolver.get(), method); + if (class_filter.Matches(accessor.GetDescriptor())) { + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + if (method.GetCodeItem() != nullptr) { + action(resolver.get(), method); + } } } } @@ -55,9 +59,12 @@ void PreciseHiddenApiFinder::AddUsesAt(const std::vector<ReflectAccessInfo>& acc } } -void PreciseHiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers) { +void PreciseHiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers, + const ClassFilter& class_filter) { // Collect reflection uses. - RunInternal(resolvers, [this] (VeridexResolver* resolver, const ClassAccessor::Method& method) { + RunInternal(resolvers, + class_filter, + [this] (VeridexResolver* resolver, const ClassAccessor::Method& method) { FlowAnalysisCollector collector(resolver, method); collector.Run(); AddUsesAt(collector.GetUses(), method.GetReference()); @@ -73,6 +80,7 @@ void PreciseHiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolv std::map<MethodReference, std::vector<ReflectAccessInfo>> current_uses = std::move(abstract_uses_); RunInternal(resolvers, + class_filter, [this, current_uses] (VeridexResolver* resolver, const ClassAccessor::Method& method) { FlowAnalysisSubstitutor substitutor(resolver, method, current_uses); diff --git a/tools/veridex/precise_hidden_api_finder.h b/tools/veridex/precise_hidden_api_finder.h index 8c5126cf1b..5254e8408e 100644 --- a/tools/veridex/precise_hidden_api_finder.h +++ b/tools/veridex/precise_hidden_api_finder.h @@ -17,6 +17,7 @@ #ifndef ART_TOOLS_VERIDEX_PRECISE_HIDDEN_API_FINDER_H_ #define ART_TOOLS_VERIDEX_PRECISE_HIDDEN_API_FINDER_H_ +#include "class_filter.h" #include "dex/method_reference.h" #include "flow_analysis.h" @@ -40,7 +41,8 @@ class PreciseHiddenApiFinder { // 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); + void Run(const std::vector<std::unique_ptr<VeridexResolver>>& app_resolvers, + const ClassFilter& app_class_filter); void Dump(std::ostream& os, HiddenApiStats* stats); @@ -48,6 +50,7 @@ class PreciseHiddenApiFinder { // Run over all methods of all dex files, and call `action` on each. void RunInternal( const std::vector<std::unique_ptr<VeridexResolver>>& resolvers, + const ClassFilter& class_filter, const std::function<void(VeridexResolver*, const ClassAccessor::Method&)>& action); // Add uses found in method `ref`. diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc index 3b6c7f9f39..bababce824 100644 --- a/tools/veridex/veridex.cc +++ b/tools/veridex/veridex.cc @@ -17,6 +17,7 @@ #include "veridex.h" #include <android-base/file.h> +#include <android-base/strings.h> #include "dex/dex_file.h" #include "dex/dex_file_loader.h" @@ -71,6 +72,7 @@ static const char* kFlagsOption = "--api-flags="; static const char* kImprecise = "--imprecise"; static const char* kTargetSdkVersion = "--target-sdk-version="; static const char* kOnlyReportSdkUses = "--only-report-sdk-uses"; +static const char* kAppClassFilter = "--app-class-filter="; struct VeridexOptions { const char* dex_file = nullptr; @@ -79,6 +81,7 @@ struct VeridexOptions { bool precise = true; int target_sdk_version = 28; /* P */ bool only_report_sdk_uses = false; + std::vector<std::string> app_class_name_filter; }; static const char* Substr(const char* str, int index) { @@ -107,6 +110,11 @@ static void ParseArgs(VeridexOptions* options, int argc, char** argv) { options->target_sdk_version = atoi(Substr(argv[i], strlen(kTargetSdkVersion))); } else if (strcmp(argv[i], kOnlyReportSdkUses) == 0) { options->only_report_sdk_uses = true; + } else if (StartsWith(argv[i], kAppClassFilter)) { + options->app_class_name_filter = android::base::Split( + Substr(argv[i], strlen(kAppClassFilter)), ","); + } else { + LOG(WARNING) << "Unknown command line argument: " << argv[i]; } } } @@ -218,17 +226,19 @@ class Veridex { std::vector<std::unique_ptr<VeridexResolver>> app_resolvers; Resolve(app_dex_files, resolver_map, type_map, &app_resolvers); + ClassFilter app_class_filter(options.app_class_name_filter); + // Find and log uses of hidden APIs. HiddenApi hidden_api(options.flags_file, options.only_report_sdk_uses); HiddenApiStats stats; HiddenApiFinder api_finder(hidden_api); - api_finder.Run(app_resolvers); + api_finder.Run(app_resolvers, app_class_filter); api_finder.Dump(std::cout, &stats, !options.precise); if (options.precise) { PreciseHiddenApiFinder precise_api_finder(hidden_api); - precise_api_finder.Run(app_resolvers); + precise_api_finder.Run(app_resolvers, app_class_filter); precise_api_finder.Dump(std::cout, &stats); } |