WIP of echidna guidance printer

Features:
        - Detect payable functions
        - Detect functions using assert/block.number/msg.sender/timestamp/msg.gas
        - Constant extraction (including binary type used)
pull/321/head
Josselin 5 years ago
parent 9922eb499a
commit 38478ac9da
  1. 17
      slither/core/declarations/function.py
  2. 19
      slither/core/variables/variable.py
  3. 1
      slither/printers/all_printers.py
  4. 0
      slither/printers/guidance/__init__.py
  5. 120
      slither/printers/guidance/echidna.py
  6. 15
      slither/printers/summary/function_ids.py

@ -113,9 +113,11 @@ class Function(ChildContract, ChildInheritance, SourceMapping):
self._all_high_level_calls = None self._all_high_level_calls = None
self._all_library_calls = None self._all_library_calls = None
self._all_low_level_calls = None self._all_low_level_calls = None
self._all_solidity_calls = None
self._all_state_variables_read = None self._all_state_variables_read = None
self._all_solidity_variables_read = None self._all_solidity_variables_read = None
self._all_state_variables_written = None self._all_state_variables_written = None
self._all_slithir_variables = None
self._all_conditional_state_variables_read = None self._all_conditional_state_variables_read = None
self._all_conditional_state_variables_read_with_loop = None self._all_conditional_state_variables_read_with_loop = None
self._all_conditional_solidity_variables_read = None self._all_conditional_solidity_variables_read = None
@ -796,6 +798,14 @@ class Function(ChildContract, ChildInheritance, SourceMapping):
lambda x: x.solidity_variables_read) lambda x: x.solidity_variables_read)
return self._all_solidity_variables_read return self._all_solidity_variables_read
def all_slithir_variables(self):
""" recursive version of slithir_variables
"""
if self._all_slithir_variables is None:
self._all_slithir_variables = self._explore_functions(
lambda x: x.slithir_variable)
return self._all_slithir_variables
def all_expressions(self): def all_expressions(self):
""" recursive version of variables_read """ recursive version of variables_read
""" """
@ -846,6 +856,13 @@ class Function(ChildContract, ChildInheritance, SourceMapping):
self._all_library_calls = self._explore_functions(lambda x: x.library_calls) self._all_library_calls = self._explore_functions(lambda x: x.library_calls)
return self._all_library_calls return self._all_library_calls
def all_solidity_calls(self):
""" recursive version of solidity calls
"""
if self._all_solidity_calls is None:
self._all_solidity_calls = self._explore_functions(lambda x: x.solidity_calls)
return self._all_solidity_calls
@staticmethod @staticmethod
def _explore_func_cond_read(func, include_loop): def _explore_func_cond_read(func, include_loop):
ret = [n.state_variables_read for n in func.nodes if n.is_conditional(include_loop)] ret = [n.state_variables_read for n in func.nodes if n.is_conditional(include_loop)]

@ -78,6 +78,25 @@ class Variable(SourceMapping):
assert isinstance(t, (Type, list)) or t is None assert isinstance(t, (Type, list)) or t is None
self._type = t self._type = t
@property
def function_name(self):
'''
Return the name of the variable as a function signature
:return:
'''
from slither.core.solidity_types import ArrayType, MappingType
variable_getter_args = ""
if type(self.type) is ArrayType:
length = 0
v = self
while type(v.type) is ArrayType:
length += 1
v = v.type
variable_getter_args = ','.join(["uint256"] * length)
elif type(self.type) is MappingType:
variable_getter_args = self.type.type_from
return f"{self.name}({variable_getter_args})"
def __str__(self): def __str__(self):
return self._name return self._name

@ -14,3 +14,4 @@ from .summary.data_depenency import DataDependency
from .summary.modifier_calls import Modifiers from .summary.modifier_calls import Modifiers
from .summary.require_calls import RequireOrAssert from .summary.require_calls import RequireOrAssert
from .summary.constructor_calls import ConstructorPrinter from .summary.constructor_calls import ConstructorPrinter
from .guidance.echidna import Echidna

@ -0,0 +1,120 @@
"""
"""
import json
from collections import defaultdict
from slither.printers.abstract_printer import AbstractPrinter
from slither.core.declarations.solidity_variables import SolidityVariableComposed, SolidityFunction
from slither.slithir.operations.binary import Binary, BinaryType
from slither.slithir.variables import Constant
def _extract_payable(slither):
ret = {}
for contract in slither.contracts:
payable_functions = [f.full_name for f in contract.functions_entry_points if f.payable]
if payable_functions:
ret[contract.name] = payable_functions
return ret
def _extract_solidity_variable_usage(slither, sol_var):
ret = {}
for contract in slither.contracts:
functions_using_sol_var = []
for f in contract.functions_entry_points:
for v in f.all_solidity_variables_read():
if v == sol_var:
functions_using_sol_var.append(f.full_name)
break
if functions_using_sol_var:
ret[contract.name] = functions_using_sol_var
return ret
def _extract_constant_functions(slither):
ret = {}
for contract in slither.contracts:
cst_functions = [f.full_name for f in contract.functions_entry_points if f.view or f.pure]
if cst_functions:
ret[contract.name] = cst_functions
return ret
def _extract_assert(slither):
ret = {}
for contract in slither.contracts:
functions_using_assert = []
for f in contract.functions_entry_points:
for v in f.all_solidity_calls():
if v == SolidityFunction('assert(bool)'):
functions_using_assert.append(f.full_name)
break
if functions_using_assert:
ret[contract.name] = functions_using_assert
return ret
def _extract_constants(slither):
ret_cst_used = defaultdict(dict)
ret_cst_used_in_binary = defaultdict(dict)
for contract in slither.contracts:
for function in contract.functions_entry_points:
all_cst_used = []
all_cst_used_in_binary = defaultdict(list)
for ir in function.all_slithir_operations():
if isinstance(ir, Binary):
for r in ir.read:
if isinstance(r, Constant):
all_cst_used_in_binary[BinaryType.str(ir.type)].append(r.value)
for r in ir.read:
if isinstance(r, Constant):
all_cst_used.append(r.value)
if all_cst_used:
ret_cst_used[contract.name][function.full_name] = all_cst_used
if all_cst_used_in_binary:
ret_cst_used_in_binary[contract.name][function.full_name] = all_cst_used_in_binary
return (ret_cst_used, ret_cst_used_in_binary)
class Echidna(AbstractPrinter):
ARGUMENT = 'echidna'
HELP = 'todo'
WIKI = 'https://github.com/trailofbits/slither/wiki/Printer-documentation#echidna'
def output(self, filename):
"""
Output the inheritance relation
_filename is not used
Args:
_filename(string)
"""
payable = _extract_payable(self.slither)
timestamp = _extract_solidity_variable_usage(self.slither,
SolidityVariableComposed('block.timestamp'))
block_number = _extract_solidity_variable_usage(self.slither,
SolidityVariableComposed('block.number'))
msg_sender = _extract_solidity_variable_usage(self.slither,
SolidityVariableComposed('msg.sender'))
msg_gas = _extract_solidity_variable_usage(self.slither,
SolidityVariableComposed('msg.gas'))
assert_usage = _extract_assert(self.slither)
cst_functions = _extract_constant_functions(self.slither)
(cst_used, cst_used_in_binary) = _extract_constants(self.slither)
d = {'payable': payable,
'timestamp': timestamp,
'block_number': block_number,
'msg_sender': msg_sender,
'msg_gas': msg_gas,
'assert': assert_usage,
'constant_functions': cst_functions,
'constants_used': cst_used,
'constants_used_in_binary': cst_used_in_binary}
print(json.dumps(d, indent=4))

@ -4,7 +4,6 @@
import collections import collections
from prettytable import PrettyTable from prettytable import PrettyTable
from slither.core.solidity_types import ArrayType, MappingType
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
from slither.utils.colors import blue, green, magenta from slither.utils.colors import blue, green, magenta
from slither.utils.function import get_function_id from slither.utils.function import get_function_id
@ -32,18 +31,8 @@ class FunctionIds(AbstractPrinter):
table.add_row([function.full_name, hex(get_function_id(function.full_name))]) table.add_row([function.full_name, hex(get_function_id(function.full_name))])
for variable in contract.state_variables: for variable in contract.state_variables:
if variable.visibility in ['public']: if variable.visibility in ['public']:
variable_getter_args = "" sig = variable.function_name
if type(variable.type) is ArrayType: table.add_row([sig, hex(get_function_id(sig))])
length = 0
v = variable
while type(v.type) is ArrayType:
length += 1
v = v.type
variable_getter_args = ','.join(["uint256"]*length)
elif type(variable.type) is MappingType:
variable_getter_args = variable.type.type_from
table.add_row([f"{variable.name}({variable_getter_args})", hex(get_function_id(f"{variable.name}({variable_getter_args})"))])
txt += str(table) + '\n' txt += str(table) + '\n'
self.info(txt) self.info(txt)

Loading…
Cancel
Save