/*
* 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_