Merge pull request #321 from crytic/dev-echidna

Echidna guidance printer
pull/340/head
Feist Josselin 5 years ago committed by GitHub
commit 07ea324a1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      slither/core/declarations/function.py
  2. 23
      slither/core/variables/state_variable.py
  3. 19
      slither/core/variables/variable.py
  4. 3
      slither/printers/all_printers.py
  5. 0
      slither/printers/guidance/__init__.py
  6. 141
      slither/printers/guidance/echidna.py
  7. 15
      slither/printers/summary/function_ids.py
  8. 4
      slither/solc_parsing/declarations/contract.py

@ -114,9 +114,11 @@ class Function(ChildContract, ChildInheritance, SourceMapping):
self._all_high_level_calls = None
self._all_library_calls = None
self._all_low_level_calls = None
self._all_solidity_calls = None
self._all_state_variables_read = None
self._all_solidity_variables_read = 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_with_loop = None
self._all_conditional_solidity_variables_read = None
@ -799,6 +801,14 @@ class Function(ChildContract, ChildInheritance, SourceMapping):
lambda x: x.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):
""" recursive version of variables_read
"""
@ -849,6 +859,13 @@ class Function(ChildContract, ChildInheritance, SourceMapping):
self._all_library_calls = self._explore_functions(lambda x: x.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
def _explore_func_cond_read(func, include_loop):
ret = [n.state_variables_read for n in func.nodes if n.is_conditional(include_loop)]

@ -4,6 +4,9 @@ from slither.utils.type import export_nested_types_from_variable
class StateVariable(ChildContract, Variable):
def __init__(self):
super(StateVariable, self).__init__()
self._node_initialization = None
def is_declared_by(self, contract):
"""
@ -61,4 +64,24 @@ class StateVariable(ChildContract, Variable):
# endregion
###################################################################################
###################################################################################
# region IRs (initialization)
###################################################################################
###################################################################################
@property
def node_initialization(self):
"""
Node for the state variable initalization
:return:
"""
return self._node_initialization
@node_initialization.setter
def node_initialization(self, node_initialization):
self._node_initialization = node_initialization
# endregion
###################################################################################
###################################################################################

@ -78,6 +78,25 @@ class Variable(SourceMapping):
assert isinstance(t, (Type, list)) or t is None
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):
return self._name

@ -13,4 +13,5 @@ from .summary.variable_order import VariableOrder
from .summary.data_depenency import DataDependency
from .summary.modifier_calls import Modifiers
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,141 @@
"""
"""
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.core.variables.state_variable import StateVariable
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]
cst_functions += [v.function_name for v in contract.state_variables if v.visibility in ['public']]
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_from_irs(irs, all_cst_used, all_cst_used_in_binary, context_explored):
for ir in irs:
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 isinstance(r, StateVariable):
if r.node_initialization:
if r.node_initialization.irs:
if r.node_initialization in context_explored:
continue
else:
context_explored.add(r.node_initialization)
_extract_constants_from_irs(r.node_initialization.irs,
all_cst_used,
all_cst_used_in_binary,
context_explored)
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)
context_explored = set()
context_explored.add(function)
_extract_constants_from_irs(function.all_slithir_operations(),
all_cst_used,
all_cst_used_in_binary,
context_explored)
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
from prettytable import PrettyTable
from slither.core.solidity_types import ArrayType, MappingType
from slither.printers.abstract_printer import AbstractPrinter
from slither.utils.colors import blue, green, magenta
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))])
for variable in contract.state_variables:
if variable.visibility in ['public']:
variable_getter_args = ""
if type(variable.type) is ArrayType:
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})"))])
sig = variable.function_name
table.add_row([sig, hex(get_function_id(sig))])
txt += str(table) + '\n'
self.info(txt)

@ -402,10 +402,12 @@ class ContractSolc04(Contract):
self._functions[constructor_variable.canonical_name] = constructor_variable
prev_node = self._create_node(constructor_variable, 0, variable_candidate)
variable_candidate.node_initialization = prev_node
counter = 1
for v in self.state_variables[idx+1:]:
if v.expression and not v.is_constant:
next_node = self._create_node(constructor_variable, counter, v)
v.node_initialization = next_node
prev_node.add_son(next_node)
next_node.add_father(prev_node)
counter += 1
@ -425,10 +427,12 @@ class ContractSolc04(Contract):
self._functions[constructor_variable.canonical_name] = constructor_variable
prev_node = self._create_node(constructor_variable, 0, variable_candidate)
variable_candidate.node_initialization = prev_node
counter = 1
for v in self.state_variables[idx+1:]:
if v.expression and v.is_constant:
next_node = self._create_node(constructor_variable, counter, v)
v.node_initialization = next_node
prev_node.add_son(next_node)
next_node.add_father(prev_node)
counter += 1

Loading…
Cancel
Save