diff options
| author | 2018-04-04 22:32:03 +0100 | |
|---|---|---|
| committer | 2018-04-06 17:02:50 +0100 | |
| commit | 2ebff0539394b1936d3c364bec4b1ffd85cd630e (patch) | |
| tree | ca32ca72b714b7abcdd4af7ec8f14e478fdb696b | |
| parent | c14f54b6d328eb23763391f98dfd776a720c4f54 (diff) | |
[veridex] Add a flow analysis pass to detect precise reflection usages.
bug: 77513322
Test: m
(cherry picked from commit 242758af3cf6eae389f43d3804acaabaa4ba93da)
Change-Id: Ifd9bed0819541ea88fc5363b5c7c2726972456ed
| -rw-r--r-- | tools/veridex/Android.bp | 2 | ||||
| -rw-r--r-- | tools/veridex/README.md | 2 | ||||
| -rwxr-xr-x | tools/veridex/appcompat.sh | 2 | ||||
| -rw-r--r-- | tools/veridex/flow_analysis.cc | 602 | ||||
| -rw-r--r-- | tools/veridex/flow_analysis.h | 147 | ||||
| -rw-r--r-- | tools/veridex/hidden_api.h | 12 | ||||
| -rw-r--r-- | tools/veridex/hidden_api_finder.cc | 65 | ||||
| -rw-r--r-- | tools/veridex/hidden_api_finder.h | 4 | ||||
| -rw-r--r-- | tools/veridex/precise_hidden_api_finder.cc | 92 | ||||
| -rw-r--r-- | tools/veridex/precise_hidden_api_finder.h | 55 | ||||
| -rw-r--r-- | tools/veridex/resolver.cc | 36 | ||||
| -rw-r--r-- | tools/veridex/resolver.h | 5 | ||||
| -rw-r--r-- | tools/veridex/veridex.cc | 66 | ||||
| -rw-r--r-- | tools/veridex/veridex.h | 38 |
14 files changed, 1075 insertions, 53 deletions
diff --git a/tools/veridex/Android.bp b/tools/veridex/Android.bp index cbf62d9e9c..570960c4da 100644 --- a/tools/veridex/Android.bp +++ b/tools/veridex/Android.bp @@ -16,8 +16,10 @@ cc_binary { name: "veridex", host_supported: true, srcs: [ + "flow_analysis.cc", "hidden_api.cc", "hidden_api_finder.cc", + "precise_hidden_api_finder.cc", "resolver.cc", "veridex.cc", ], diff --git a/tools/veridex/README.md b/tools/veridex/README.md index 0f91b08771..f85a51bb48 100644 --- a/tools/veridex/README.md +++ b/tools/veridex/README.md @@ -11,4 +11,4 @@ To build it: > make appcompat To run it: -> ./art/tools/veridex/appcompat.sh test.apk +> ./art/tools/veridex/appcompat.sh --dex-file=test.apk diff --git a/tools/veridex/appcompat.sh b/tools/veridex/appcompat.sh index f75aa4f0d0..31a8654b58 100755 --- a/tools/veridex/appcompat.sh +++ b/tools/veridex/appcompat.sh @@ -48,4 +48,4 @@ ${ANDROID_HOST_OUT}/bin/veridex \ --blacklist=${PACKAGING}/hiddenapi-blacklist.txt \ --light-greylist=${PACKAGING}/hiddenapi-light-greylist.txt \ --dark-greylist=${PACKAGING}/hiddenapi-dark-greylist.txt \ - --dex-file=$1 + $@ diff --git a/tools/veridex/flow_analysis.cc b/tools/veridex/flow_analysis.cc new file mode 100644 index 0000000000..abd0b9b28c --- /dev/null +++ b/tools/veridex/flow_analysis.cc @@ -0,0 +1,602 @@ +/* + * 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 "flow_analysis.h" + +#include "dex/bytecode_utils.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_instruction-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_exception_helpers.h" +#include "resolver.h" +#include "veridex.h" + +namespace art { + + +void VeriFlowAnalysis::SetAsBranchTarget(uint32_t dex_pc) { + if (dex_registers_[dex_pc] == nullptr) { + dex_registers_[dex_pc].reset( + new std::vector<RegisterValue>(code_item_accessor_.RegistersSize())); + } +} + +bool VeriFlowAnalysis::IsBranchTarget(uint32_t dex_pc) { + return dex_registers_[dex_pc] != nullptr; +} + +bool VeriFlowAnalysis::MergeRegisterValues(uint32_t dex_pc) { + // TODO: Do the merging. Right now, just return that we should continue + // the iteration if the instruction has not been visited. + return !instruction_infos_[dex_pc].has_been_visited; +} + +void VeriFlowAnalysis::SetVisited(uint32_t dex_pc) { + instruction_infos_[dex_pc].has_been_visited = true; +} + +void VeriFlowAnalysis::FindBranches() { + SetAsBranchTarget(0); + + if (code_item_accessor_.TriesSize() != 0) { + // TODO: We need to mark the range of dex pcs as flowing in the handlers. + /* + for (const DexFile::TryItem& try_item : code_item_accessor_.TryItems()) { + uint32_t dex_pc_start = try_item.start_addr_; + uint32_t dex_pc_end = dex_pc_start + try_item.insn_count_; + } + */ + + // Create branch targets for exception handlers. + const uint8_t* handlers_ptr = code_item_accessor_.GetCatchHandlerData(); + uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); + for (uint32_t idx = 0; idx < handlers_size; ++idx) { + CatchHandlerIterator iterator(handlers_ptr); + for (; iterator.HasNext(); iterator.Next()) { + SetAsBranchTarget(iterator.GetHandlerAddress()); + } + handlers_ptr = iterator.EndDataPointer(); + } + } + + // Iterate over all instructions and find branching instructions. + for (const DexInstructionPcPair& pair : code_item_accessor_) { + const uint32_t dex_pc = pair.DexPc(); + const Instruction& instruction = pair.Inst(); + + if (instruction.IsBranch()) { + SetAsBranchTarget(dex_pc + instruction.GetTargetOffset()); + } else if (instruction.IsSwitch()) { + DexSwitchTable table(instruction, dex_pc); + for (DexSwitchTableIterator s_it(table); !s_it.Done(); s_it.Advance()) { + SetAsBranchTarget(dex_pc + s_it.CurrentTargetOffset()); + if (table.ShouldBuildDecisionTree() && !s_it.IsLast()) { + SetAsBranchTarget(s_it.GetDexPcForCurrentIndex()); + } + } + } + } +} + +void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, + RegisterSource kind, + VeriClass* cls, + uint32_t source_id) { + current_registers_[dex_register] = RegisterValue( + kind, DexFileReference(&resolver_->GetDexFile(), source_id), cls); +} + +void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, const RegisterValue& value) { + current_registers_[dex_register] = value; +} + +void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, const VeriClass* cls) { + current_registers_[dex_register] = + RegisterValue(RegisterSource::kNone, DexFileReference(nullptr, 0), cls); +} + +const RegisterValue& VeriFlowAnalysis::GetRegister(uint32_t dex_register) { + return current_registers_[dex_register]; +} + +RegisterValue VeriFlowAnalysis::GetReturnType(uint32_t method_index) { + const DexFile& dex_file = resolver_->GetDexFile(); + const DexFile::MethodId& method_id = dex_file.GetMethodId(method_index); + const DexFile::ProtoId& proto_id = dex_file.GetMethodPrototype(method_id); + VeriClass* cls = resolver_->GetVeriClass(proto_id.return_type_idx_); + return RegisterValue(RegisterSource::kMethod, DexFileReference(&dex_file, method_index), cls); +} + +RegisterValue VeriFlowAnalysis::GetFieldType(uint32_t field_index) { + const DexFile& dex_file = resolver_->GetDexFile(); + const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index); + VeriClass* cls = resolver_->GetVeriClass(field_id.type_idx_); + return RegisterValue(RegisterSource::kField, DexFileReference(&dex_file, field_index), cls); +} + +void VeriFlowAnalysis::AnalyzeCode() { + std::vector<uint32_t> work_list; + work_list.push_back(0); + // Iterate over the code. + // When visiting unconditional branches (goto), move to that instruction. + // When visiting conditional branches, move to one destination, and put the other + // in the worklist. + while (!work_list.empty()) { + uint32_t dex_pc = work_list.back(); + work_list.pop_back(); + CHECK(IsBranchTarget(dex_pc)); + current_registers_ = *dex_registers_[dex_pc].get(); + while (true) { + const uint16_t* insns = code_item_accessor_.Insns() + dex_pc; + const Instruction& inst = *Instruction::At(insns); + ProcessDexInstruction(inst); + SetVisited(dex_pc); + + int opcode_flags = Instruction::FlagsOf(inst.Opcode()); + if ((opcode_flags & Instruction::kContinue) != 0) { + if ((opcode_flags & Instruction::kBranch) != 0) { + uint32_t branch_dex_pc = dex_pc + inst.GetTargetOffset(); + if (MergeRegisterValues(branch_dex_pc)) { + work_list.push_back(branch_dex_pc); + } + } + dex_pc += inst.SizeInCodeUnits(); + } else if ((opcode_flags & Instruction::kBranch) != 0) { + dex_pc += inst.GetTargetOffset(); + DCHECK(IsBranchTarget(dex_pc)); + } else { + break; + } + + if (IsBranchTarget(dex_pc)) { + if (MergeRegisterValues(dex_pc)) { + current_registers_ = *dex_registers_[dex_pc].get(); + } else { + break; + } + } + } + } +} + +void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { + switch (instruction.Opcode()) { + case Instruction::CONST_4: + case Instruction::CONST_16: + case Instruction::CONST: + case Instruction::CONST_HIGH16: { + int32_t register_index = instruction.VRegA(); + UpdateRegister(register_index, VeriClass::integer_); + break; + } + + case Instruction::CONST_WIDE_16: + case Instruction::CONST_WIDE_32: + case Instruction::CONST_WIDE: + case Instruction::CONST_WIDE_HIGH16: { + int32_t register_index = instruction.VRegA(); + UpdateRegister(register_index, VeriClass::long_); + break; + } + + case Instruction::MOVE: + case Instruction::MOVE_FROM16: + case Instruction::MOVE_16: { + UpdateRegister(instruction.VRegA(), GetRegister(instruction.VRegB())); + break; + } + + case Instruction::MOVE_WIDE: + case Instruction::MOVE_WIDE_FROM16: + case Instruction::MOVE_WIDE_16: { + UpdateRegister(instruction.VRegA(), GetRegister(instruction.VRegB())); + break; + } + + case Instruction::MOVE_OBJECT: + case Instruction::MOVE_OBJECT_16: + case Instruction::MOVE_OBJECT_FROM16: { + UpdateRegister(instruction.VRegA(), GetRegister(instruction.VRegB())); + break; + } + case Instruction::CONST_CLASS: { + UpdateRegister(instruction.VRegA_21c(), + RegisterSource::kClass, + VeriClass::class_, + instruction.VRegB_21c()); + break; + } + case Instruction::CONST_STRING: { + UpdateRegister(instruction.VRegA_21c(), + RegisterSource::kString, + VeriClass::string_, + instruction.VRegB_21c()); + break; + } + + case Instruction::CONST_STRING_JUMBO: { + UpdateRegister(instruction.VRegA_31c(), + RegisterSource::kString, + VeriClass::string_, + instruction.VRegB_31c()); + break; + } + case Instruction::INVOKE_DIRECT: + case Instruction::INVOKE_INTERFACE: + case Instruction::INVOKE_STATIC: + case Instruction::INVOKE_SUPER: + case Instruction::INVOKE_VIRTUAL: { + VeriMethod method = resolver_->GetMethod(instruction.VRegB_35c()); + uint32_t args[5]; + instruction.GetVarArgs(args); + if (method == VeriClass::forName_) { + RegisterValue value = GetRegister(args[0]); + last_result_ = RegisterValue( + value.GetSource(), value.GetDexFileReference(), VeriClass::class_); + } else if (IsGetField(method)) { + RegisterValue cls = GetRegister(args[0]); + RegisterValue name = GetRegister(args[1]); + field_uses_.push_back(std::make_pair(cls, name)); + last_result_ = GetReturnType(instruction.VRegB_35c()); + } else if (IsGetMethod(method)) { + RegisterValue cls = GetRegister(args[0]); + RegisterValue name = GetRegister(args[1]); + method_uses_.push_back(std::make_pair(cls, name)); + last_result_ = GetReturnType(instruction.VRegB_35c()); + } else if (method == VeriClass::getClass_) { + RegisterValue obj = GetRegister(args[0]); + last_result_ = RegisterValue( + obj.GetSource(), obj.GetDexFileReference(), VeriClass::class_); + } else { + last_result_ = GetReturnType(instruction.VRegB_35c()); + } + 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: { + last_result_ = GetReturnType(instruction.VRegB_3rc()); + break; + } + + case Instruction::MOVE_RESULT: + case Instruction::MOVE_RESULT_WIDE: + case Instruction::MOVE_RESULT_OBJECT: { + UpdateRegister(instruction.VRegA(), last_result_); + break; + } + case Instruction::RETURN_VOID: + case Instruction::RETURN_OBJECT: + case Instruction::RETURN_WIDE: + case Instruction::RETURN: { + break; + } + #define IF_XX(cond) \ + case Instruction::IF_##cond: break; \ + case Instruction::IF_##cond##Z: break + + IF_XX(EQ); + IF_XX(NE); + IF_XX(LT); + IF_XX(LE); + IF_XX(GT); + IF_XX(GE); + + case Instruction::GOTO: + case Instruction::GOTO_16: + case Instruction::GOTO_32: { + break; + } + case Instruction::INVOKE_POLYMORPHIC: { + // TODO + break; + } + + case Instruction::INVOKE_POLYMORPHIC_RANGE: { + // TODO + break; + } + + case Instruction::NEG_INT: + case Instruction::NEG_LONG: + case Instruction::NEG_FLOAT: + case Instruction::NEG_DOUBLE: + case Instruction::NOT_INT: + case Instruction::NOT_LONG: { + UpdateRegister(instruction.VRegA(), VeriClass::integer_); + break; + } + + case Instruction::INT_TO_LONG: + case Instruction::INT_TO_FLOAT: + case Instruction::INT_TO_DOUBLE: + case Instruction::LONG_TO_INT: + case Instruction::LONG_TO_FLOAT: + case Instruction::LONG_TO_DOUBLE: + case Instruction::FLOAT_TO_INT: + case Instruction::FLOAT_TO_LONG: + case Instruction::FLOAT_TO_DOUBLE: + case Instruction::DOUBLE_TO_INT: + case Instruction::DOUBLE_TO_LONG: + case Instruction::DOUBLE_TO_FLOAT: + case Instruction::INT_TO_BYTE: + case Instruction::INT_TO_SHORT: + case Instruction::INT_TO_CHAR: { + UpdateRegister(instruction.VRegA(), VeriClass::integer_); + break; + } + + case Instruction::ADD_INT: + case Instruction::ADD_LONG: + case Instruction::ADD_DOUBLE: + case Instruction::ADD_FLOAT: + case Instruction::SUB_INT: + case Instruction::SUB_LONG: + case Instruction::SUB_FLOAT: + case Instruction::SUB_DOUBLE: + case Instruction::MUL_INT: + case Instruction::MUL_LONG: + case Instruction::MUL_FLOAT: + case Instruction::MUL_DOUBLE: + case Instruction::DIV_INT: + case Instruction::DIV_LONG: + case Instruction::DIV_FLOAT: + case Instruction::DIV_DOUBLE: + case Instruction::REM_INT: + case Instruction::REM_LONG: + case Instruction::REM_FLOAT: + case Instruction::REM_DOUBLE: + case Instruction::AND_INT: + case Instruction::AND_LONG: + case Instruction::SHL_INT: + case Instruction::SHL_LONG: + case Instruction::SHR_INT: + case Instruction::SHR_LONG: + case Instruction::USHR_INT: + case Instruction::USHR_LONG: + case Instruction::OR_INT: + case Instruction::OR_LONG: + case Instruction::XOR_INT: + case Instruction::XOR_LONG: { + UpdateRegister(instruction.VRegA(), VeriClass::integer_); + break; + } + + case Instruction::ADD_INT_2ADDR: + case Instruction::ADD_LONG_2ADDR: + case Instruction::ADD_DOUBLE_2ADDR: + case Instruction::ADD_FLOAT_2ADDR: + case Instruction::SUB_INT_2ADDR: + case Instruction::SUB_LONG_2ADDR: + case Instruction::SUB_FLOAT_2ADDR: + case Instruction::SUB_DOUBLE_2ADDR: + case Instruction::MUL_INT_2ADDR: + case Instruction::MUL_LONG_2ADDR: + case Instruction::MUL_FLOAT_2ADDR: + case Instruction::MUL_DOUBLE_2ADDR: + case Instruction::DIV_INT_2ADDR: + case Instruction::DIV_LONG_2ADDR: + case Instruction::REM_INT_2ADDR: + case Instruction::REM_LONG_2ADDR: + case Instruction::REM_FLOAT_2ADDR: + case Instruction::REM_DOUBLE_2ADDR: + case Instruction::SHL_INT_2ADDR: + case Instruction::SHL_LONG_2ADDR: + case Instruction::SHR_INT_2ADDR: + case Instruction::SHR_LONG_2ADDR: + case Instruction::USHR_INT_2ADDR: + case Instruction::USHR_LONG_2ADDR: + case Instruction::DIV_FLOAT_2ADDR: + case Instruction::DIV_DOUBLE_2ADDR: + case Instruction::AND_INT_2ADDR: + case Instruction::AND_LONG_2ADDR: + case Instruction::OR_INT_2ADDR: + case Instruction::OR_LONG_2ADDR: + case Instruction::XOR_INT_2ADDR: + case Instruction::XOR_LONG_2ADDR: { + UpdateRegister(instruction.VRegA(), VeriClass::integer_); + break; + } + + case Instruction::ADD_INT_LIT16: + case Instruction::AND_INT_LIT16: + case Instruction::OR_INT_LIT16: + case Instruction::XOR_INT_LIT16: + case Instruction::RSUB_INT: + case Instruction::MUL_INT_LIT16: + case Instruction::DIV_INT_LIT16: + case Instruction::REM_INT_LIT16: { + UpdateRegister(instruction.VRegA(), VeriClass::integer_); + break; + } + + case Instruction::ADD_INT_LIT8: + case Instruction::AND_INT_LIT8: + case Instruction::OR_INT_LIT8: + case Instruction::XOR_INT_LIT8: + case Instruction::RSUB_INT_LIT8: + case Instruction::MUL_INT_LIT8: + case Instruction::DIV_INT_LIT8: + case Instruction::REM_INT_LIT8: + case Instruction::SHL_INT_LIT8: + case Instruction::SHR_INT_LIT8: { + case Instruction::USHR_INT_LIT8: { + UpdateRegister(instruction.VRegA(), VeriClass::integer_); + break; + } + + case Instruction::NEW_INSTANCE: { + VeriClass* cls = resolver_->GetVeriClass(dex::TypeIndex(instruction.VRegB_21c())); + UpdateRegister(instruction.VRegA(), cls); + break; + } + + case Instruction::NEW_ARRAY: { + dex::TypeIndex type_index(instruction.VRegC_22c()); + VeriClass* cls = resolver_->GetVeriClass(type_index); + UpdateRegister(instruction.VRegA_22c(), cls); + break; + } + + case Instruction::FILLED_NEW_ARRAY: { + dex::TypeIndex type_index(instruction.VRegB_35c()); + VeriClass* cls = resolver_->GetVeriClass(type_index); + UpdateRegister(instruction.VRegA_22c(), cls); + break; + } + + case Instruction::FILLED_NEW_ARRAY_RANGE: { + dex::TypeIndex type_index(instruction.VRegB_3rc()); + uint32_t register_index = instruction.VRegC_3rc(); + VeriClass* cls = resolver_->GetVeriClass(type_index); + UpdateRegister(register_index, cls); + break; + } + + case Instruction::FILL_ARRAY_DATA: { + break; + } + + case Instruction::CMP_LONG: + case Instruction::CMPG_FLOAT: + case Instruction::CMPG_DOUBLE: + case Instruction::CMPL_FLOAT: + case Instruction::CMPL_DOUBLE: + UpdateRegister(instruction.VRegA(), VeriClass::integer_); + break; + } + + case Instruction::NOP: + 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: { + UpdateRegister(instruction.VRegA_22c(), GetFieldType(instruction.VRegC_22c())); + 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: { + 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: { + UpdateRegister(instruction.VRegA_22c(), GetFieldType(instruction.VRegC_22c())); + 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: { + break; + } + +#define ARRAY_XX(kind, anticipated_type) \ + case Instruction::AGET##kind: { \ + UpdateRegister(instruction.VRegA_23x(), anticipated_type); \ + break; \ + } \ + case Instruction::APUT##kind: { \ + break; \ + } + + ARRAY_XX(, VeriClass::integer_); + ARRAY_XX(_WIDE, VeriClass::long_); + ARRAY_XX(_BOOLEAN, VeriClass::boolean_); + ARRAY_XX(_BYTE, VeriClass::byte_); + ARRAY_XX(_CHAR, VeriClass::char_); + ARRAY_XX(_SHORT, VeriClass::short_); + + case Instruction::AGET_OBJECT: { + // TODO: take the component type. + UpdateRegister(instruction.VRegA_23x(), VeriClass::object_); + break; + } + + case Instruction::APUT_OBJECT: { + break; + } + + case Instruction::ARRAY_LENGTH: { + UpdateRegister(instruction.VRegA_12x(), VeriClass::integer_); + break; + } + + case Instruction::MOVE_EXCEPTION: { + UpdateRegister(instruction.VRegA_11x(), VeriClass::throwable_); + break; + } + + case Instruction::THROW: { + break; + } + + case Instruction::INSTANCE_OF: { + uint8_t destination = instruction.VRegA_22c(); + UpdateRegister(destination, VeriClass::boolean_); + break; + } + + case Instruction::CHECK_CAST: { + uint8_t reference = instruction.VRegA_21c(); + dex::TypeIndex type_index(instruction.VRegB_21c()); + UpdateRegister(reference, resolver_->GetVeriClass(type_index)); + break; + } + + case Instruction::MONITOR_ENTER: + case Instruction::MONITOR_EXIT: { + break; + } + + case Instruction::SPARSE_SWITCH: + case Instruction::PACKED_SWITCH: + break; + + default: + break; + } +} + +void VeriFlowAnalysis::Run() { + FindBranches(); + AnalyzeCode(); +} + +} // namespace art diff --git a/tools/veridex/flow_analysis.h b/tools/veridex/flow_analysis.h new file mode 100644 index 0000000000..c065fb8c24 --- /dev/null +++ b/tools/veridex/flow_analysis.h @@ -0,0 +1,147 @@ +/* + * 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_FLOW_ANALYSIS_H_ +#define ART_TOOLS_VERIDEX_FLOW_ANALYSIS_H_ + +#include "dex/code_item_accessors.h" +#include "dex/dex_file_reference.h" +#include "dex/method_reference.h" +#include "veridex.h" + +namespace art { + +class VeridexClass; +class VeridexResolver; + +/** + * The source where a dex register comes from. + */ +enum class RegisterSource { + kParameter, + kField, + kMethod, + kClass, + kString, + kNone +}; + +/** + * Abstract representation of a dex register value. + */ +class RegisterValue { + public: + RegisterValue() : source_(RegisterSource::kNone), reference_(nullptr, 0), type_(nullptr) {} + RegisterValue(RegisterSource source, DexFileReference reference, const VeriClass* type) + : source_(source), reference_(reference), type_(type) {} + + RegisterSource GetSource() const { return source_; } + DexFileReference GetDexFileReference() const { return reference_; } + const VeriClass* GetType() const { return type_; } + + const char* ToString() const { + switch (source_) { + case RegisterSource::kString: + return reference_.dex_file->StringDataByIdx(dex::StringIndex(reference_.index)); + case RegisterSource::kClass: + return reference_.dex_file->StringByTypeIdx(dex::TypeIndex(reference_.index)); + default: + return "<unknown>"; + } + } + + private: + RegisterSource source_; + DexFileReference reference_; + const VeriClass* type_; +}; + +struct InstructionInfo { + bool has_been_visited; +}; + +class VeriFlowAnalysis { + public: + VeriFlowAnalysis(VeridexResolver* resolver, + const CodeItemDataAccessor& code_item_accessor) + : resolver_(resolver), + code_item_accessor_(code_item_accessor), + dex_registers_(code_item_accessor.InsnsSizeInCodeUnits()), + instruction_infos_(code_item_accessor.InsnsSizeInCodeUnits()) {} + + void Run(); + + const std::vector<std::pair<RegisterValue, RegisterValue>>& GetFieldUses() const { + return field_uses_; + } + + const std::vector<std::pair<RegisterValue, RegisterValue>>& GetMethodUses() const { + return method_uses_; + } + + private: + // Find all branches in the code. + void FindBranches(); + + // Analyze all non-deead instructions in the code. + void AnalyzeCode(); + + // Set the instruction at the given pc as a branch target. + void SetAsBranchTarget(uint32_t dex_pc); + + // Whether the instruction at the given pc is a branch target. + bool IsBranchTarget(uint32_t dex_pc); + + // Merge the register values at the given pc with `current_registers`. + // Return whether the register values have changed, and the instruction needs + // to be visited again. + bool MergeRegisterValues(uint32_t dex_pc); + + void UpdateRegister( + uint32_t dex_register, RegisterSource kind, VeriClass* cls, uint32_t source_id); + void UpdateRegister(uint32_t dex_register, const RegisterValue& value); + void UpdateRegister(uint32_t dex_register, const VeriClass* cls); + const RegisterValue& GetRegister(uint32_t dex_register); + void ProcessDexInstruction(const Instruction& inst); + void SetVisited(uint32_t dex_pc); + RegisterValue GetReturnType(uint32_t method_index); + RegisterValue GetFieldType(uint32_t field_index); + + VeridexResolver* resolver_; + const CodeItemDataAccessor& code_item_accessor_; + + // Vector of register values for all branch targets. + std::vector<std::unique_ptr<std::vector<RegisterValue>>> dex_registers_; + + // The current values of dex registers. + std::vector<RegisterValue> current_registers_; + + // Information on each instruction useful for the analysis. + std::vector<InstructionInfo> instruction_infos_; + + // The value of invoke instructions, to be fetched when visiting move-result. + RegisterValue last_result_; + + // List of reflection field uses found. + std::vector<std::pair<RegisterValue, RegisterValue>> field_uses_; + + // List of reflection method uses found. + std::vector<std::pair<RegisterValue, RegisterValue>> method_uses_; +}; + +} // namespace art + +#endif // ART_TOOLS_VERIDEX_FLOW_ANALYSIS_H_ diff --git a/tools/veridex/hidden_api.h b/tools/veridex/hidden_api.h index 5893b8ae33..4c67768a00 100644 --- a/tools/veridex/hidden_api.h +++ b/tools/veridex/hidden_api.h @@ -18,6 +18,7 @@ #define ART_TOOLS_VERIDEX_HIDDEN_API_H_ #include "dex/hidden_api_access_flags.h" +#include "dex/method_reference.h" #include <ostream> #include <set> @@ -58,6 +59,10 @@ class HiddenApi { static std::string GetApiFieldName(const DexFile& dex_file, uint32_t field_index); + static std::string GetApiMethodName(MethodReference ref) { + return HiddenApi::GetApiMethodName(*ref.dex_file, ref.index); + } + private: static bool IsInList(const std::string& name, const std::set<std::string>& list) { return list.find(name) != list.end(); @@ -70,6 +75,13 @@ class HiddenApi { std::set<std::string> dark_greylist_; }; +struct HiddenApiStats { + uint32_t count = 0; + uint32_t reflection_count = 0; + uint32_t linking_count = 0; + uint32_t api_counts[4] = { 0, 0, 0, 0 }; +}; + } // namespace art #endif // ART_TOOLS_VERIDEX_HIDDEN_API_H_ diff --git a/tools/veridex/hidden_api_finder.cc b/tools/veridex/hidden_api_finder.cc index 4885e02769..b9be618ed7 100644 --- a/tools/veridex/hidden_api_finder.cc +++ b/tools/veridex/hidden_api_finder.cc @@ -193,32 +193,26 @@ void HiddenApiFinder::CollectAccesses(VeridexResolver* resolver) { } } -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) { +void HiddenApiFinder::Dump(std::ostream& os, + HiddenApiStats* stats, + bool dump_reflection) { 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}; + stats->linking_count = method_locations_.size() + field_locations_.size(); // 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):"; + stats->api_counts[api_list]++; + os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):"; os << std::endl; for (const MethodReference& ref : pair.second) { - os << kPrefix << GetApiMethodName(ref) << std::endl; + os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl; } os << std::endl; } @@ -226,42 +220,35 @@ void HiddenApiFinder::Dump(std::ostream& os) { // 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):"; + stats->api_counts[api_list]++; + os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):"; os << std::endl; for (const MethodReference& ref : pair.second) { - os << kPrefix << GetApiMethodName(ref) << std::endl; + os << kPrefix << HiddenApi::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; + if (dump_reflection) { + // 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); + stats->api_counts[api_list]++; + if (api_list != HiddenApiAccessFlags::kWhitelist) { + stats->reflection_count++; + os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name + << " potential use(s):"; + os << std::endl; + for (const MethodReference& ref : reflection_locations_[name]) { + os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl; + } + os << 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 index 243079c187..f7d3dc832d 100644 --- a/tools/veridex/hidden_api_finder.h +++ b/tools/veridex/hidden_api_finder.h @@ -27,6 +27,7 @@ namespace art { class HiddenApi; +struct HiddenApiStats; class VeridexResolver; /** @@ -40,11 +41,12 @@ class HiddenApiFinder { // hidden API uses. void Run(const std::vector<std::unique_ptr<VeridexResolver>>& app_resolvers); + void Dump(std::ostream& os, HiddenApiStats* stats, bool dump_reflection); + 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_; diff --git a/tools/veridex/precise_hidden_api_finder.cc b/tools/veridex/precise_hidden_api_finder.cc new file mode 100644 index 0000000000..2092af3d75 --- /dev/null +++ b/tools/veridex/precise_hidden_api_finder.cc @@ -0,0 +1,92 @@ +/* + * 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 "precise_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 "flow_analysis.h" +#include "hidden_api.h" +#include "resolver.h" +#include "veridex.h" + +#include <iostream> + +namespace art { + +void PreciseHiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers) { + for (const std::unique_ptr<VeridexResolver>& resolver : resolvers) { + 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); + VeriFlowAnalysis ana(resolver.get(), code_item_accessor); + ana.Run(); + if (!ana.GetFieldUses().empty()) { + field_uses_[MethodReference(&dex_file, it.GetMemberIndex())] = ana.GetFieldUses(); + } + if (!ana.GetMethodUses().empty()) { + method_uses_[MethodReference(&dex_file, it.GetMemberIndex())] = ana.GetMethodUses(); + } + } + } + } +} + +void PreciseHiddenApiFinder::Dump(std::ostream& os, HiddenApiStats* stats) { + static const char* kPrefix = " "; + for (auto kinds : { field_uses_, method_uses_ }) { + for (auto it : kinds) { + MethodReference ref = it.first; + for (const std::pair<RegisterValue, RegisterValue>& info : it.second) { + if ((info.first.GetSource() == RegisterSource::kClass || + info.first.GetSource() == RegisterSource::kString) && + info.second.GetSource() == RegisterSource::kString) { + std::string cls(info.first.ToString()); + std::string name(info.second.ToString()); + std::string full_name = cls + "->" + name; + HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name); + stats->api_counts[api_list]++; + if (api_list != HiddenApiAccessFlags::kWhitelist) { + ++stats->reflection_count; + os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name + << " use:"; + os << std::endl; + os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl; + os << std::endl; + } + } + } + } + } +} + +} // namespace art diff --git a/tools/veridex/precise_hidden_api_finder.h b/tools/veridex/precise_hidden_api_finder.h new file mode 100644 index 0000000000..22744a6f1f --- /dev/null +++ b/tools/veridex/precise_hidden_api_finder.h @@ -0,0 +1,55 @@ +/* + * 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_PRECISE_HIDDEN_API_FINDER_H_ +#define ART_TOOLS_VERIDEX_PRECISE_HIDDEN_API_FINDER_H_ + +#include "dex/method_reference.h" +#include "flow_analysis.h" + +#include <iostream> +#include <map> +#include <set> +#include <string> + +namespace art { + +class HiddenApi; +struct HiddenApiStats; +class VeridexResolver; + +/** + * Reports known uses of hidden APIs from reflection. + */ +class PreciseHiddenApiFinder { + public: + explicit PreciseHiddenApiFinder(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); + + void Dump(std::ostream& os, HiddenApiStats* stats); + + private: + const HiddenApi& hidden_api_; + std::map<MethodReference, std::vector<std::pair<RegisterValue, RegisterValue>>> field_uses_; + std::map<MethodReference, std::vector<std::pair<RegisterValue, RegisterValue>>> method_uses_; +}; + +} // namespace art + +#endif // ART_TOOLS_VERIDEX_PRECISE_HIDDEN_API_FINDER_H_ diff --git a/tools/veridex/resolver.cc b/tools/veridex/resolver.cc index 13dda5c199..9113039b04 100644 --- a/tools/veridex/resolver.cc +++ b/tools/veridex/resolver.cc @@ -59,6 +59,14 @@ void VeridexResolver::Run() { static bool HasSameNameAndSignature(const DexFile& dex_file, const DexFile::MethodId& method_id, const char* method_name, + const char* type) { + return strcmp(method_name, dex_file.GetMethodName(method_id)) == 0 && + strcmp(type, dex_file.GetMethodSignature(method_id).ToString().c_str()) == 0; +} + +static bool HasSameNameAndSignature(const DexFile& dex_file, + const DexFile::MethodId& method_id, + const char* method_name, const Signature& signature) { return strcmp(method_name, dex_file.GetMethodName(method_id)) == 0 && dex_file.GetMethodSignature(method_id) == signature; @@ -241,6 +249,34 @@ VeriField VeridexResolver::LookupFieldIn(const VeriClass& kls, return nullptr; } +VeriMethod VeridexResolver::LookupDeclaredMethodIn(const VeriClass& kls, + const char* method_name, + const char* type) const { + if (kls.IsPrimitive()) { + return nullptr; + } + if (kls.IsArray()) { + return nullptr; + } + VeridexResolver* resolver = GetResolverOf(kls); + const DexFile& other_dex_file = resolver->dex_file_; + const uint8_t* class_data = other_dex_file.GetClassData(*kls.GetClassDef()); + if (class_data != nullptr) { + ClassDataItemIterator it(other_dex_file, class_data); + it.SkipAllFields(); + for (; it.HasNextMethod(); it.Next()) { + const DexFile::MethodId& other_method_id = other_dex_file.GetMethodId(it.GetMemberIndex()); + if (HasSameNameAndSignature(other_dex_file, + other_method_id, + method_name, + type)) { + return it.DataPointer(); + } + } + } + return nullptr; +} + VeriMethod VeridexResolver::GetMethod(uint32_t method_index) { VeriMethod method_info = method_infos_[method_index]; if (method_info == nullptr) { diff --git a/tools/veridex/resolver.h b/tools/veridex/resolver.h index 06c8aa70c5..52b15fe0ed 100644 --- a/tools/veridex/resolver.h +++ b/tools/veridex/resolver.h @@ -66,6 +66,11 @@ class VeridexResolver { const char* field_name, const char* field_type); + // Lookup a method declared in `kls`. + VeriMethod LookupDeclaredMethodIn(const VeriClass& kls, + const char* method_name, + const char* signature) const; + // Resolve all type_id/method_id/field_id. void ResolveAll(); diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc index 16e9f0e55b..6e72faaf57 100644 --- a/tools/veridex/veridex.cc +++ b/tools/veridex/veridex.cc @@ -22,6 +22,7 @@ #include "dex/dex_file_loader.h" #include "hidden_api.h" #include "hidden_api_finder.h" +#include "precise_hidden_api_finder.h" #include "resolver.h" #include <sstream> @@ -47,8 +48,18 @@ VeriClass* VeriClass::float_ = &f_; VeriClass* VeriClass::double_ = &d_; VeriClass* VeriClass::long_ = &j_; VeriClass* VeriClass::void_ = &v_; + // Will be set after boot classpath has been resolved. VeriClass* VeriClass::object_ = nullptr; +VeriClass* VeriClass::class_ = nullptr; +VeriClass* VeriClass::string_ = nullptr; +VeriClass* VeriClass::throwable_ = nullptr; +VeriMethod VeriClass::forName_ = nullptr; +VeriMethod VeriClass::getField_ = nullptr; +VeriMethod VeriClass::getDeclaredField_ = nullptr; +VeriMethod VeriClass::getMethod_ = nullptr; +VeriMethod VeriClass::getDeclaredMethod_ = nullptr; +VeriMethod VeriClass::getClass_ = nullptr; struct VeridexOptions { const char* dex_file = nullptr; @@ -56,6 +67,7 @@ struct VeridexOptions { const char* blacklist = nullptr; const char* light_greylist = nullptr; const char* dark_greylist = nullptr; + bool precise = true; }; static const char* Substr(const char* str, int index) { @@ -76,6 +88,7 @@ static void ParseArgs(VeridexOptions* options, int argc, char** argv) { static const char* kBlacklistOption = "--blacklist="; static const char* kDarkGreylistOption = "--dark-greylist="; static const char* kLightGreylistOption = "--light-greylist="; + static const char* kImprecise = "--imprecise"; for (int i = 0; i < argc; ++i) { if (StartsWith(argv[i], kDexFileOption)) { @@ -88,6 +101,8 @@ static void ParseArgs(VeridexOptions* options, int argc, char** argv) { options->dark_greylist = Substr(argv[i], strlen(kDarkGreylistOption)); } else if (StartsWith(argv[i], kLightGreylistOption)) { options->light_greylist = Substr(argv[i], strlen(kLightGreylistOption)); + } else if (strcmp(argv[i], kImprecise) == 0) { + options->precise = false; } } } @@ -157,21 +172,70 @@ class Veridex { std::vector<std::unique_ptr<VeridexResolver>> boot_resolvers; Resolve(boot_dex_files, resolver_map, type_map, &boot_resolvers); - // Now that boot classpath has been resolved, fill j.l.Object. + // Now that boot classpath has been resolved, fill classes and reflection + // methods. VeriClass::object_ = type_map["Ljava/lang/Object;"]; + VeriClass::class_ = type_map["Ljava/lang/Class;"]; + VeriClass::string_ = type_map["Ljava/lang/String;"]; + VeriClass::throwable_ = type_map["Ljava/lang/Throwable;"]; + VeriClass::forName_ = boot_resolvers[0]->LookupDeclaredMethodIn( + *VeriClass::class_, "forName", "(Ljava/lang/String;)Ljava/lang/Class;"); + VeriClass::getField_ = boot_resolvers[0]->LookupDeclaredMethodIn( + *VeriClass::class_, "getField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;"); + VeriClass::getDeclaredField_ = boot_resolvers[0]->LookupDeclaredMethodIn( + *VeriClass::class_, "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;"); + VeriClass::getMethod_ = boot_resolvers[0]->LookupDeclaredMethodIn( + *VeriClass::class_, + "getMethod", + "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"); + VeriClass::getDeclaredMethod_ = boot_resolvers[0]->LookupDeclaredMethodIn( + *VeriClass::class_, + "getDeclaredMethod", + "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"); + VeriClass::getClass_ = boot_resolvers[0]->LookupDeclaredMethodIn( + *VeriClass::object_, "getClass", "()Ljava/lang/Class;"); std::vector<std::unique_ptr<VeridexResolver>> app_resolvers; Resolve(app_dex_files, resolver_map, type_map, &app_resolvers); // Find and log uses of hidden APIs. HiddenApi hidden_api(options.blacklist, options.dark_greylist, options.light_greylist); + HiddenApiStats stats; + HiddenApiFinder api_finder(hidden_api); api_finder.Run(app_resolvers); + 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.Dump(std::cout, &stats); + } + + DumpSummaryStats(std::cout, stats); + + if (options.precise) { + std::cout << "To run an analysis that can give more reflection accesses, " << std::endl + << "but could include false positives, pass the --imprecise flag. " << std::endl; + } return 0; } private: + static void DumpSummaryStats(std::ostream& os, const HiddenApiStats& stats) { + static const char* kPrefix = " "; + os << stats.count << " hidden API(s) used: " + << stats.linking_count << " linked against, " + << stats.reflection_count << " through reflection" << std::endl; + os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kBlacklist] + << " in blacklist" << std::endl; + os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kDarkGreylist] + << " in dark greylist" << std::endl; + os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kLightGreylist] + << " in light greylist" << std::endl; + } + static bool Load(const std::string& filename, std::string& content, std::vector<std::unique_ptr<const DexFile>>* dex_files, diff --git a/tools/veridex/veridex.h b/tools/veridex/veridex.h index 0c928ab166..75e4845293 100644 --- a/tools/veridex/veridex.h +++ b/tools/veridex/veridex.h @@ -25,6 +25,18 @@ namespace art { /** + * Abstraction for fields defined in dex files. Currently, that's a pointer into their + * `encoded_field` description. + */ +using VeriField = const uint8_t*; + +/** + * Abstraction for methods defined in dex files. Currently, that's a pointer into their + * `encoded_method` description. + */ +using VeriMethod = const uint8_t*; + +/** * Abstraction for classes defined, or implicitly defined (for arrays and primitives) * in dex files. */ @@ -52,6 +64,9 @@ class VeriClass { const DexFile::ClassDef* GetClassDef() const { return class_def_; } static VeriClass* object_; + static VeriClass* class_; + static VeriClass* string_; + static VeriClass* throwable_; static VeriClass* boolean_; static VeriClass* byte_; static VeriClass* char_; @@ -62,23 +77,26 @@ class VeriClass { static VeriClass* long_; static VeriClass* void_; + static VeriMethod forName_; + static VeriMethod getField_; + static VeriMethod getDeclaredField_; + static VeriMethod getMethod_; + static VeriMethod getDeclaredMethod_; + static VeriMethod getClass_; + private: Primitive::Type kind_; uint8_t dimensions_; const DexFile::ClassDef* class_def_; }; -/** - * Abstraction for fields defined in dex files. Currently, that's a pointer into their - * `encoded_field` description. - */ -using VeriField = const uint8_t*; +inline bool IsGetMethod(VeriMethod method) { + return method == VeriClass::getMethod_ || method == VeriClass::getDeclaredMethod_; +} -/** - * Abstraction for methods defined in dex files. Currently, that's a pointer into their - * `encoded_method` description. - */ -using VeriMethod = const uint8_t*; +inline bool IsGetField(VeriMethod method) { + return method == VeriClass::getField_ || method == VeriClass::getDeclaredField_; +} /** * Map from name to VeriClass to quickly lookup classes. |