diff --git a/slither/analyses/data_dependency/data_dependency.py b/slither/analyses/data_dependency/data_dependency.py index 1412c24757..95cd10754f 100644 --- a/slither/analyses/data_dependency/data_dependency.py +++ b/slither/analyses/data_dependency/data_dependency.py @@ -98,6 +98,21 @@ def is_tainted_ssa(variable, context, only_unprotected=False): 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) + ''' + assert isinstance(context, (Contract, Function)) + assert isinstance(only_unprotected, bool) + if only_unprotected: + return context.context[KEY_NON_SSA].get(variable, []) + return context.context[KEY_NON_SSA_UNPROTECTED].get(variable, []) + # endregion ################################################################################### ################################################################################### @@ -162,8 +177,11 @@ def compute_dependency_contract(contract, slither): for function in contract.all_functions_called: compute_dependency_function(function) - propagate_function(contract, function, KEY_SSA) - propagate_function(contract, function, KEY_SSA_UNPROTECTED) + propagate_function(contract, function, KEY_SSA, KEY_NON_SSA) + propagate_function(contract, + function, + KEY_SSA_UNPROTECTED, + KEY_NON_SSA_UNPROTECTED) if function.visibility in ['public', 'external']: [slither.context[KEY_INPUT].add(p) for p in function.parameters] @@ -172,7 +190,8 @@ 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): +def propagate_function(contract, function, context_key, context_key_non_ssa): + transitive_close_dependencies(function, context_key, context_key_non_ssa) # Propage data dependency data_depencencies = function.context[context_key] for (key, values) in data_depencencies.items(): @@ -181,22 +200,26 @@ def propagate_function(contract, function, context_key): else: contract.context[context_key][key].union(values) -def propagate_contract(contract, context_key, context_key_non_ssa): +def transitive_close_dependencies(context, context_key, context_key_non_ssa): # transitive closure changed = True while changed: changed = False # Need to create new set() as its changed during iteration - data_depencencies = {k: set([v for v in values]) for k, values in contract.context[context_key].items()} + data_depencencies = {k: set([v for v in values]) for k, values in context.context[context_key].items()} for key, items in data_depencencies.items(): for item in items: if item in data_depencencies: - additional_items = contract.context[context_key][item] + additional_items = context.context[context_key][item] for additional_item in additional_items: if not additional_item in items and additional_item != key: changed = True - contract.context[context_key][key].add(additional_item) - contract.context[context_key_non_ssa] = convert_to_non_ssa(contract.context[context_key]) + context.context[context_key][key].add(additional_item) + context.context[context_key_non_ssa] = convert_to_non_ssa(context.context[context_key]) + + +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]: diff --git a/slither/printers/all_printers.py b/slither/printers/all_printers.py index 6f21d95ffc..8e7e8bce2c 100644 --- a/slither/printers/all_printers.py +++ b/slither/printers/all_printers.py @@ -10,3 +10,4 @@ from .functions.cfg import CFG from .summary.function_ids import FunctionIds from .summary.variables_order import VariablesOrder +from .summary.data_depenency import DataDependency diff --git a/slither/printers/summary/data_depenency.py b/slither/printers/summary/data_depenency.py new file mode 100644 index 0000000000..8bc2d2bd4d --- /dev/null +++ b/slither/printers/summary/data_depenency.py @@ -0,0 +1,46 @@ +""" + Module printing summary of the contract +""" + +from prettytable import PrettyTable +from slither.printers.abstract_printer import AbstractPrinter +from slither.analyses.data_dependency.data_dependency import get_dependencies +from slither.slithir.variables import TemporaryVariable, ReferenceVariable + +def _get(v, c): + return [d.name for d in get_dependencies(v, c) if not isinstance(d, (TemporaryVariable, + ReferenceVariable))] + +class DataDependency(AbstractPrinter): + + ARGUMENT = 'data-dependency' + HELP = 'Print the data dependencies of the variables' + + WIKI = 'https://github.com/trailofbits/slither/wiki/Printer-documentation#data-dependencies' + + def output(self, _filename): + """ + _filename is not used + Args: + _filename(string) + """ + + txt = '' + for c in self.contracts: + txt += "\nContract %s\n"%c.name + table = PrettyTable(['Variable', 'Depenencies']) + for v in c.state_variables: + table.add_row([v.name, _get(v, c)]) + + txt += str(table) + + txt += "\n" + for f in c.functions_and_modifiers_not_inherited: + txt += "\nFunction %s\n"%f.full_name + table = PrettyTable(['Variable', 'Depenencies']) + for v in f.variables: + table.add_row([v.name, _get(v, f)]) + for v in c.state_variables: + table.add_row([v.canonical_name, _get(v, f)]) + txt += str(table) + self.info(txt)