diff --git a/slither/analyses/data_dependency/data_dependency.py b/slither/analyses/data_dependency/data_dependency.py index 288bb63b7..8a3c7cf3d 100644 --- a/slither/analyses/data_dependency/data_dependency.py +++ b/slither/analyses/data_dependency/data_dependency.py @@ -1,16 +1,20 @@ """ Compute the data depenency between all the SSA variables """ +from typing import Union, Set, Dict + from slither.core.declarations import (Contract, Enum, Function, SolidityFunction, SolidityVariable, SolidityVariableComposed, Structure) +from slither.core.variables.variable import Variable from slither.slithir.operations import Index, OperationWithLValue, InternalCall from slither.slithir.variables import (Constant, LocalIRVariable, ReferenceVariable, ReferenceVariableSSA, - StateIRVariable, TemporaryVariable, + StateIRVariable, TemporaryVariableSSA, TupleVariableSSA) from slither.core.solidity_types.type import Type + ################################################################################### ################################################################################### # region User APIs @@ -38,6 +42,7 @@ def is_dependent(variable, source, context, only_unprotected=False): return variable in context[KEY_NON_SSA_UNPROTECTED] and source in context[KEY_NON_SSA_UNPROTECTED][variable] return variable in context[KEY_NON_SSA] and source in context[KEY_NON_SSA][variable] + def is_dependent_ssa(variable, source, context, only_unprotected=False): ''' Args: @@ -58,11 +63,13 @@ def is_dependent_ssa(variable, source, context, only_unprotected=False): return variable in context[KEY_SSA_UNPROTECTED] and source in context[KEY_SSA_UNPROTECTED][variable] return variable in context[KEY_SSA] and source in context[KEY_SSA][variable] + GENERIC_TAINT = {SolidityVariableComposed('msg.sender'), SolidityVariableComposed('msg.value'), SolidityVariableComposed('msg.data'), SolidityVariableComposed('tx.origin')} + def is_tainted(variable, context, only_unprotected=False, ignore_generic_taint=False): ''' Args: @@ -82,6 +89,7 @@ def is_tainted(variable, context, only_unprotected=False, ignore_generic_taint=F taints |= GENERIC_TAINT return variable in taints or any(is_dependent(variable, t, context, only_unprotected) for t in taints) + def is_tainted_ssa(variable, context, only_unprotected=False, ignore_generic_taint=False): ''' Args: @@ -102,20 +110,71 @@ def is_tainted_ssa(variable, context, only_unprotected=False, ignore_generic_tai return variable in taints or any(is_dependent_ssa(variable, t, context, only_unprotected) for t in taints) -def get_dependencies(variable, context, only_unprotected=False): - ''' - Args: - variable - context (Contract|Function) - only_unprotected (bool): True only unprotected function are considered - Returns: - list(Variable) - ''' +def get_dependencies( + variable: Variable, context: Union[Contract, Function], only_unprotected: bool = False) -> Set[Variable]: + """ + Return the variables for which `variable` depends on. + + :param variable: The target + :param context: Either a function (interprocedural) or a contract (inter transactional) + :param only_unprotected: True if consider only protected functions + :return: set(Variable) + """ + assert isinstance(context, (Contract, Function)) + assert isinstance(only_unprotected, bool) + if only_unprotected: + return context.context[KEY_NON_SSA_UNPROTECTED].get(variable, set()) + return context.context[KEY_NON_SSA].get(variable, set()) + + +def get_all_dependencies( + context: Union[Contract, Function], only_unprotected: bool = False) -> Dict[Variable, Set[Variable]]: + """ + Return the dictionary of dependencies. + + :param context: Either a function (interprocedural) or a contract (inter transactional) + :param only_unprotected: True if consider only protected functions + :return: Dict(Variable, set(Variable)) + """ assert isinstance(context, (Contract, Function)) assert isinstance(only_unprotected, bool) if only_unprotected: - return context.context[KEY_NON_SSA_UNPROTECTED].get(variable, []) - return context.context[KEY_NON_SSA].get(variable, []) + return context.context[KEY_NON_SSA_UNPROTECTED] + return context.context[KEY_NON_SSA] + + +def get_dependencies_ssa( + variable: Variable, context: Union[Contract, Function], only_unprotected: bool = False) -> Set[Variable]: + """ + Return the variables for which `variable` depends on (SSA version). + + :param variable: The target (must be SSA variable) + :param context: Either a function (interprocedural) or a contract (inter transactional) + :param only_unprotected: True if consider only protected functions + :return: set(Variable) + """ + assert isinstance(context, (Contract, Function)) + assert isinstance(only_unprotected, bool) + if only_unprotected: + return context.context[KEY_SSA_UNPROTECTED].get(variable, set()) + return context.context[KEY_SSA].get(variable, set()) + + +def get_all_dependencies_ssa( + context: Union[Contract, Function], only_unprotected: bool = False) -> Dict[Variable, Set[Variable]]: + """ + Return the dictionary of dependencies. + + :param context: Either a function (interprocedural) or a contract (inter transactional) + :param only_unprotected: True if consider only protected functions + :return: Dict(Variable, set(Variable)) + """ + assert isinstance(context, (Contract, Function)) + assert isinstance(only_unprotected, bool) + if only_unprotected: + return context.context[KEY_SSA_UNPROTECTED] + return context.context[KEY_SSA] + # endregion ################################################################################### @@ -156,6 +215,7 @@ def pprint_dependency(context): for v in values: print('\t- {} ({})'.format(v, hex(id(v)))) + # endregion ################################################################################### ################################################################################### @@ -164,13 +224,13 @@ def pprint_dependency(context): ################################################################################### def compute_dependency(slither): - slither.context[KEY_INPUT] = set() slither.context[KEY_INPUT_SSA] = set() for contract in slither.contracts: compute_dependency_contract(contract, slither) + def compute_dependency_contract(contract, slither): if KEY_SSA in contract.context: return @@ -194,6 +254,7 @@ def compute_dependency_contract(contract, slither): propagate_contract(contract, KEY_SSA, KEY_NON_SSA) propagate_contract(contract, KEY_SSA_UNPROTECTED, KEY_NON_SSA_UNPROTECTED) + def propagate_function(contract, function, context_key, context_key_non_ssa): transitive_close_dependencies(function, context_key, context_key_non_ssa) # Propage data dependency @@ -204,6 +265,7 @@ def propagate_function(contract, function, context_key, context_key_non_ssa): else: contract.context[context_key][key].union(values) + def transitive_close_dependencies(context, context_key, context_key_non_ssa): # transitive closure changed = True @@ -225,6 +287,7 @@ def transitive_close_dependencies(context, context_key, context_key_non_ssa): def propagate_contract(contract, context_key, context_key_non_ssa): transitive_close_dependencies(contract, context_key, context_key_non_ssa) + def add_dependency(lvalue, function, ir, is_protected): if not lvalue in function.context[KEY_SSA]: function.context[KEY_SSA][lvalue] = set() @@ -263,12 +326,14 @@ def compute_dependency_function(function): function.context[KEY_NON_SSA] = convert_to_non_ssa(function.context[KEY_SSA]) function.context[KEY_NON_SSA_UNPROTECTED] = convert_to_non_ssa(function.context[KEY_SSA_UNPROTECTED]) + def convert_variable_to_non_ssa(v): if isinstance(v, (LocalIRVariable, StateIRVariable, TemporaryVariableSSA, ReferenceVariableSSA, TupleVariableSSA)): return v.non_ssa_version assert isinstance(v, (Constant, SolidityVariable, Contract, Enum, SolidityFunction, Structure, Function, Type)) return v + def convert_to_non_ssa(data_depencies): # Need to create new set() as its changed during iteration ret = dict()