You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
229 lines
7.3 KiB
229 lines
7.3 KiB
/*
|
|
* 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/class_accessor.h"
|
|
#include "dex/code_item_accessors.h"
|
|
#include "dex/dex_file_reference.h"
|
|
#include "dex/method_reference.h"
|
|
#include "hidden_api.h"
|
|
#include "resolver.h"
|
|
#include "veridex.h"
|
|
|
|
namespace art {
|
|
|
|
/**
|
|
* The source where a dex register comes from.
|
|
*/
|
|
enum class RegisterSource {
|
|
kParameter,
|
|
kField,
|
|
kMethod,
|
|
kClass,
|
|
kString,
|
|
kConstant,
|
|
kNone
|
|
};
|
|
|
|
/**
|
|
* Abstract representation of a dex register value.
|
|
*/
|
|
class RegisterValue {
|
|
public:
|
|
RegisterValue() : source_(RegisterSource::kNone),
|
|
value_(0),
|
|
reference_(nullptr, 0),
|
|
type_(nullptr) {}
|
|
RegisterValue(RegisterSource source, DexFileReference reference, const VeriClass* type)
|
|
: source_(source), value_(0), reference_(reference), type_(type) {}
|
|
|
|
RegisterValue(RegisterSource source,
|
|
uint32_t value,
|
|
DexFileReference reference,
|
|
const VeriClass* type)
|
|
: source_(source), value_(value), reference_(reference), type_(type) {}
|
|
|
|
RegisterSource GetSource() const { return source_; }
|
|
DexFileReference GetDexFileReference() const { return reference_; }
|
|
const VeriClass* GetType() const { return type_; }
|
|
uint32_t GetParameterIndex() const {
|
|
CHECK(IsParameter());
|
|
return value_;
|
|
}
|
|
uint32_t GetConstant() const {
|
|
CHECK(IsConstant());
|
|
return value_;
|
|
}
|
|
bool IsParameter() const { return source_ == RegisterSource::kParameter; }
|
|
bool IsClass() const { return source_ == RegisterSource::kClass; }
|
|
bool IsString() const { return source_ == RegisterSource::kString; }
|
|
bool IsConstant() const { return source_ == RegisterSource::kConstant; }
|
|
|
|
std::string ToString() const {
|
|
switch (source_) {
|
|
case RegisterSource::kString: {
|
|
const char* str = reference_.dex_file->StringDataByIdx(dex::StringIndex(reference_.index));
|
|
if (type_ == VeriClass::class_) {
|
|
// 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.
|
|
return HiddenApi::ToInternalName(str);
|
|
} else {
|
|
return str;
|
|
}
|
|
}
|
|
case RegisterSource::kClass:
|
|
return reference_.dex_file->StringByTypeIdx(dex::TypeIndex(reference_.index));
|
|
case RegisterSource::kParameter:
|
|
return std::string("Parameter of ") + reference_.dex_file->PrettyMethod(reference_.index);
|
|
default:
|
|
return "<unknown>";
|
|
}
|
|
}
|
|
|
|
private:
|
|
RegisterSource source_;
|
|
uint32_t value_;
|
|
DexFileReference reference_;
|
|
const VeriClass* type_;
|
|
};
|
|
|
|
struct InstructionInfo {
|
|
bool has_been_visited;
|
|
};
|
|
|
|
class VeriFlowAnalysis {
|
|
public:
|
|
VeriFlowAnalysis(VeridexResolver* resolver, const ClassAccessor::Method& method);
|
|
|
|
void Run();
|
|
|
|
virtual RegisterValue AnalyzeInvoke(const Instruction& instruction, bool is_range) = 0;
|
|
virtual void AnalyzeFieldSet(const Instruction& instruction) = 0;
|
|
virtual ~VeriFlowAnalysis() {}
|
|
|
|
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);
|
|
void UpdateRegister(uint32_t dex_register, int32_t value, const VeriClass* cls);
|
|
void ProcessDexInstruction(const Instruction& inst);
|
|
void SetVisited(uint32_t dex_pc);
|
|
RegisterValue GetFieldType(uint32_t field_index);
|
|
|
|
int GetBranchFlags(const Instruction& instruction) const;
|
|
|
|
protected:
|
|
const RegisterValue& GetRegister(uint32_t dex_register) const;
|
|
RegisterValue GetReturnType(uint32_t method_index);
|
|
|
|
VeridexResolver* resolver_;
|
|
|
|
private:
|
|
const uint32_t method_id_;
|
|
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_;
|
|
};
|
|
|
|
struct ReflectAccessInfo {
|
|
RegisterValue cls;
|
|
RegisterValue name;
|
|
bool is_method;
|
|
|
|
ReflectAccessInfo(RegisterValue c, RegisterValue n, bool is_method)
|
|
: cls(c), name(n), is_method(is_method) {}
|
|
|
|
bool IsConcrete() const {
|
|
// We capture RegisterSource::kString for the class, for example in Class.forName.
|
|
return (cls.IsClass() || cls.IsString()) && name.IsString();
|
|
}
|
|
};
|
|
|
|
// Collects all reflection uses.
|
|
class FlowAnalysisCollector : public VeriFlowAnalysis {
|
|
public:
|
|
FlowAnalysisCollector(VeridexResolver* resolver, const ClassAccessor::Method& method)
|
|
: VeriFlowAnalysis(resolver, method) {}
|
|
|
|
const std::vector<ReflectAccessInfo>& GetUses() const {
|
|
return uses_;
|
|
}
|
|
|
|
RegisterValue AnalyzeInvoke(const Instruction& instruction, bool is_range) override;
|
|
void AnalyzeFieldSet(const Instruction& instruction) override;
|
|
|
|
private:
|
|
// List of reflection uses found, concrete and abstract.
|
|
std::vector<ReflectAccessInfo> uses_;
|
|
};
|
|
|
|
// Substitutes reflection uses by new ones.
|
|
class FlowAnalysisSubstitutor : public VeriFlowAnalysis {
|
|
public:
|
|
FlowAnalysisSubstitutor(VeridexResolver* resolver,
|
|
const ClassAccessor::Method& method,
|
|
const std::map<MethodReference, std::vector<ReflectAccessInfo>>& accesses)
|
|
: VeriFlowAnalysis(resolver, method), accesses_(accesses) {}
|
|
|
|
const std::vector<ReflectAccessInfo>& GetUses() const {
|
|
return uses_;
|
|
}
|
|
|
|
RegisterValue AnalyzeInvoke(const Instruction& instruction, bool is_range) override;
|
|
void AnalyzeFieldSet(const Instruction& instruction) override;
|
|
|
|
private:
|
|
// List of reflection uses found, concrete and abstract.
|
|
std::vector<ReflectAccessInfo> uses_;
|
|
// The abstract uses we are trying to subsititute.
|
|
const std::map<MethodReference, std::vector<ReflectAccessInfo>>& accesses_;
|
|
};
|
|
|
|
} // namespace art
|
|
|
|
#endif // ART_TOOLS_VERIDEX_FLOW_ANALYSIS_H_
|