Revisit *Calls API

pull/2555/head
Simone 2 months ago
parent 809d1b5155
commit 7fd0e5883c
  1. 69
      slither/core/cfg/node.py
  2. 22
      slither/core/declarations/contract.py
  3. 92
      slither/core/declarations/function.py
  4. 3
      slither/detectors/assembly/incorrect_return.py
  5. 2
      slither/detectors/attributes/locked_ether.py
  6. 8
      slither/detectors/erc/erc20/arbitrary_send_erc20.py
  7. 6
      slither/detectors/functions/dead_code.py
  8. 2
      slither/detectors/functions/modifier.py
  9. 6
      slither/detectors/functions/out_of_order_retryable.py
  10. 4
      slither/detectors/functions/protected_variable.py
  11. 2
      slither/detectors/functions/suicidal.py
  12. 9
      slither/detectors/reentrancy/reentrancy.py
  13. 2
      slither/detectors/statements/assert_state_change.py
  14. 2
      slither/detectors/statements/divide_before_multiply.py
  15. 4
      slither/detectors/statements/unprotected_upgradeable.py
  16. 25
      slither/printers/call/call_graph.py
  17. 6
      slither/printers/functions/authorization.py
  18. 4
      slither/printers/guidance/echidna.py
  19. 12
      slither/printers/summary/modifier_calls.py
  20. 4
      slither/printers/summary/when_not_paused.py
  21. 5
      slither/tools/flattening/flattening.py
  22. 10
      slither/tools/possible_paths/possible_paths.py
  23. 13
      slither/utils/upgradeability.py

@ -46,12 +46,6 @@ from slither.slithir.variables import (
if TYPE_CHECKING:
from slither.slithir.variables.variable import SlithIRVariable
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.utils.type_helpers import (
InternalCallType,
HighLevelCallType,
LibraryCallType,
LowLevelCallType,
)
from slither.core.cfg.scope import Scope
from slither.core.scope.scope import FileScope
@ -153,11 +147,11 @@ class Node(SourceMapping): # pylint: disable=too-many-public-methods
self._ssa_vars_written: List["SlithIRVariable"] = []
self._ssa_vars_read: List["SlithIRVariable"] = []
self._internal_calls: List[Union["Function", "SolidityFunction"]] = []
self._solidity_calls: List[SolidityFunction] = []
self._high_level_calls: List["HighLevelCallType"] = [] # contains library calls
self._library_calls: List["LibraryCallType"] = []
self._low_level_calls: List["LowLevelCallType"] = []
self._internal_calls: List[InternalCall] = [] # contains solidity calls
self._solidity_calls: List[SolidityCall] = []
self._high_level_calls: List[Tuple[Contract, HighLevelCall]] = [] # contains library calls
self._library_calls: List[LibraryCall] = []
self._low_level_calls: List[LowLevelCall] = []
self._external_calls_as_expressions: List[Expression] = []
self._internal_calls_as_expressions: List[Expression] = []
self._irs: List[Operation] = []
@ -226,8 +220,9 @@ class Node(SourceMapping): # pylint: disable=too-many-public-methods
@property
def will_return(self) -> bool:
if not self.sons and self.type != NodeType.THROW:
if SolidityFunction("revert()") not in self.solidity_calls:
if SolidityFunction("revert(string)") not in self.solidity_calls:
solidity_calls = [ir.function for ir in self.solidity_calls]
if SolidityFunction("revert()") not in solidity_calls:
if SolidityFunction("revert(string)") not in solidity_calls:
return True
return False
@ -373,44 +368,38 @@ class Node(SourceMapping): # pylint: disable=too-many-public-methods
###################################################################################
@property
def internal_calls(self) -> List["InternalCallType"]:
def internal_calls(self) -> List[InternalCall]:
"""
list(Function or SolidityFunction): List of internal/soldiity function calls
list(InternalCall): List of IR operations with internal/solidity function calls
"""
return list(self._internal_calls)
@property
def solidity_calls(self) -> List[SolidityFunction]:
def solidity_calls(self) -> List[SolidityCall]:
"""
list(SolidityFunction): List of Soldity calls
list(SolidityCall): List of IR operations with solidity calls
"""
return list(self._solidity_calls)
@property
def high_level_calls(self) -> List["HighLevelCallType"]:
def high_level_calls(self) -> List[HighLevelCall]:
"""
list((Contract, Function|Variable)):
List of high level calls (external calls).
A variable is called in case of call to a public state variable
list(HighLevelCall): List of IR operations with high level calls (external calls).
Include library calls
"""
return list(self._high_level_calls)
@property
def library_calls(self) -> List["LibraryCallType"]:
def library_calls(self) -> List[LibraryCall]:
"""
list((Contract, Function)):
Include library calls
list(LibraryCall): List of IR operations with library calls.
"""
return list(self._library_calls)
@property
def low_level_calls(self) -> List["LowLevelCallType"]:
def low_level_calls(self) -> List[LowLevelCall]:
"""
list((Variable|SolidityVariable, str)): List of low_level call
A low level call is defined by
- the variable called
- the name of the function (call/delegatecall/codecall)
list(LowLevelCall): List of IR operations with low_level call
"""
return list(self._low_level_calls)
@ -529,8 +518,8 @@ class Node(SourceMapping): # pylint: disable=too-many-public-methods
bool: True if the node has a require or assert call
"""
return any(
c.name in ["require(bool)", "require(bool,string)", "assert(bool)"]
for c in self.internal_calls
ir.function.name in ["require(bool)", "require(bool,string)", "assert(bool)"]
for ir in self.internal_calls
)
def contains_if(self, include_loop: bool = True) -> bool:
@ -894,11 +883,11 @@ class Node(SourceMapping): # pylint: disable=too-many-public-methods
self._vars_written.append(var)
if isinstance(ir, InternalCall):
self._internal_calls.append(ir.function)
self._internal_calls.append(ir)
if isinstance(ir, SolidityCall):
# TODO: consider removing dependancy of solidity_call to internal_call
self._solidity_calls.append(ir.function)
self._internal_calls.append(ir.function)
self._solidity_calls.append(ir)
self._internal_calls.append(ir)
if (
isinstance(ir, SolidityCall)
and ir.function == SolidityFunction("sstore(uint256,uint256)")
@ -916,22 +905,22 @@ class Node(SourceMapping): # pylint: disable=too-many-public-methods
self._vars_read.append(ir.arguments[0])
if isinstance(ir, LowLevelCall):
assert isinstance(ir.destination, (Variable, SolidityVariable))
self._low_level_calls.append((ir.destination, str(ir.function_name.value)))
self._low_level_calls.append(ir)
elif isinstance(ir, HighLevelCall) and not isinstance(ir, LibraryCall):
# Todo investigate this if condition
# It does seem right to compare against a contract
# This might need a refactoring
if isinstance(ir.destination.type, Contract):
self._high_level_calls.append((ir.destination.type, ir.function))
self._high_level_calls.append((ir.destination.type, ir))
elif ir.destination == SolidityVariable("this"):
func = self.function
# Can't use this in a top level function
assert isinstance(func, FunctionContract)
self._high_level_calls.append((func.contract, ir.function))
self._high_level_calls.append((func.contract, ir))
else:
try:
# Todo this part needs more tests and documentation
self._high_level_calls.append((ir.destination.type.type, ir.function))
self._high_level_calls.append((ir.destination.type.type, ir))
except AttributeError as error:
# pylint: disable=raise-missing-from
raise SlitherException(
@ -940,8 +929,8 @@ class Node(SourceMapping): # pylint: disable=too-many-public-methods
elif isinstance(ir, LibraryCall):
assert isinstance(ir.destination, Contract)
assert isinstance(ir.function, Function)
self._high_level_calls.append((ir.destination, ir.function))
self._library_calls.append((ir.destination, ir.function))
self._high_level_calls.append((ir.destination, ir))
self._library_calls.append(ir)
self._vars_read = list(set(self._vars_read))
self._state_vars_read = [v for v in self._vars_read if isinstance(v, StateVariable)]

@ -29,7 +29,6 @@ from slither.utils.tests_pattern import is_test_contract
# pylint: disable=too-many-lines,too-many-instance-attributes,import-outside-toplevel,too-many-nested-blocks
if TYPE_CHECKING:
from slither.utils.type_helpers import LibraryCallType, HighLevelCallType, InternalCallType
from slither.core.declarations import (
Enum,
EventContract,
@ -39,6 +38,7 @@ if TYPE_CHECKING:
FunctionContract,
CustomErrorContract,
)
from slither.slithir.operations import HighLevelCall, LibraryCall
from slither.slithir.variables.variable import SlithIRVariable
from slither.core.variables import Variable, StateVariable
from slither.core.compilation_unit import SlitherCompilationUnit
@ -106,7 +106,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
self._is_incorrectly_parsed: bool = False
self._available_functions_as_dict: Optional[Dict[str, "Function"]] = None
self._all_functions_called: Optional[List["InternalCallType"]] = None
self._all_functions_called: Optional[List["Function"]] = None
self.compilation_unit: "SlitherCompilationUnit" = compilation_unit
self.file_scope: "FileScope" = scope
@ -1023,15 +1023,21 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
###################################################################################
@property
def all_functions_called(self) -> List["InternalCallType"]:
def all_functions_called(self) -> List["Function"]:
"""
list(Function): List of functions reachable from the contract
Includes super, and private/internal functions not shadowed
"""
from slither.slithir.operations import Operation
if self._all_functions_called is None:
all_functions = [f for f in self.functions + self.modifiers if not f.is_shadowed] # type: ignore
all_callss = [f.all_internal_calls() for f in all_functions] + [list(all_functions)]
all_calls = [item for sublist in all_callss for item in sublist]
all_calls = [
item.function if isinstance(item, Operation) else item
for sublist in all_callss
for item in sublist
]
all_calls = list(set(all_calls))
all_constructors = [c.constructor for c in self.inheritance if c.constructor]
@ -1069,18 +1075,18 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
return list(set(all_state_variables_read))
@property
def all_library_calls(self) -> List["LibraryCallType"]:
def all_library_calls(self) -> List["LibraryCall"]:
"""
list((Contract, Function): List all of the libraries func called
list(LibraryCall): List all of the libraries func called
"""
all_high_level_callss = [f.all_library_calls() for f in self.functions + self.modifiers] # type: ignore
all_high_level_calls = [item for sublist in all_high_level_callss for item in sublist]
return list(set(all_high_level_calls))
@property
def all_high_level_calls(self) -> List["HighLevelCallType"]:
def all_high_level_calls(self) -> List[Tuple["Contract", "HighLevelCall"]]:
"""
list((Contract, Function|Variable)): List all of the external high level calls
list(Tuple("Contract", "HighLevelCall")): List all of the external high level calls
"""
all_high_level_callss = [f.all_high_level_calls() for f in self.functions + self.modifiers] # type: ignore
all_high_level_calls = [item for sublist in all_high_level_callss for item in sublist]

@ -31,19 +31,20 @@ from slither.utils.utils import unroll
# pylint: disable=import-outside-toplevel,too-many-instance-attributes,too-many-statements,too-many-lines
if TYPE_CHECKING:
from slither.utils.type_helpers import (
InternalCallType,
LowLevelCallType,
HighLevelCallType,
LibraryCallType,
)
from slither.core.declarations import Contract, FunctionContract
from slither.core.cfg.node import Node, NodeType
from slither.core.variables.variable import Variable
from slither.slithir.variables.variable import SlithIRVariable
from slither.slithir.variables import LocalIRVariable
from slither.core.expressions.expression import Expression
from slither.slithir.operations import Operation
from slither.slithir.operations import (
HighLevelCall,
InternalCall,
LibraryCall,
LowLevelCall,
SolidityCall,
Operation,
)
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.scope.scope import FileScope
@ -149,11 +150,11 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
self._vars_read_or_written: List["Variable"] = []
self._solidity_vars_read: List["SolidityVariable"] = []
self._state_vars_written: List["StateVariable"] = []
self._internal_calls: List["InternalCallType"] = []
self._solidity_calls: List["SolidityFunction"] = []
self._low_level_calls: List["LowLevelCallType"] = []
self._high_level_calls: List["HighLevelCallType"] = []
self._library_calls: List["LibraryCallType"] = []
self._internal_calls: List["InternalCall"] = []
self._solidity_calls: List["SolidityCall"] = []
self._low_level_calls: List["LowLevelCall"] = []
self._high_level_calls: List[Tuple["Contract", "HighLevelCall"]] = []
self._library_calls: List["LibraryCall"] = []
self._external_calls_as_expressions: List["Expression"] = []
self._expression_vars_read: List["Expression"] = []
self._expression_vars_written: List["Expression"] = []
@ -169,11 +170,11 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
self._all_expressions: Optional[List["Expression"]] = None
self._all_slithir_operations: Optional[List["Operation"]] = None
self._all_internals_calls: Optional[List["InternalCallType"]] = None
self._all_high_level_calls: Optional[List["HighLevelCallType"]] = None
self._all_library_calls: Optional[List["LibraryCallType"]] = None
self._all_low_level_calls: Optional[List["LowLevelCallType"]] = None
self._all_solidity_calls: Optional[List["SolidityFunction"]] = None
self._all_internals_calls: Optional[List["InternalCall"]] = None
self._all_high_level_calls: Optional[List[Tuple["Contract", "HighLevelCall"]]] = None
self._all_library_calls: Optional[List["LibraryCall"]] = None
self._all_low_level_calls: Optional[List["LowLevelCall"]] = None
self._all_solidity_calls: Optional[List["SolidityCall"]] = None
self._all_variables_read: Optional[List["Variable"]] = None
self._all_variables_written: Optional[List["Variable"]] = None
self._all_state_variables_read: Optional[List["StateVariable"]] = None
@ -857,43 +858,42 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
###################################################################################
@property
def internal_calls(self) -> List["InternalCallType"]:
def internal_calls(self) -> List["InternalCall"]:
"""
list(Function or SolidityFunction): List of function calls (that does not create a transaction)
list(InternalCall): List of IR operations for internal calls
"""
return list(self._internal_calls)
@property
def solidity_calls(self) -> List[SolidityFunction]:
def solidity_calls(self) -> List["SolidityCall"]:
"""
list(SolidityFunction): List of Soldity calls
list(SolidityCall): List of IR operations for Solidity calls
"""
return list(self._solidity_calls)
@property
def high_level_calls(self) -> List["HighLevelCallType"]:
def high_level_calls(self) -> List[Tuple["Contract", "HighLevelCall"]]:
"""
list((Contract, Function|Variable)):
List of high level calls (external calls).
list(Tuple(Contract, "HighLevelCall")): List of call target contract and IR of the high level call
A variable is called in case of call to a public state variable
Include library calls
"""
return list(self._high_level_calls)
@property
def library_calls(self) -> List["LibraryCallType"]:
def library_calls(self) -> List["LibraryCall"]:
"""
list((Contract, Function)):
list(LibraryCall): List of IR operations for library calls
"""
return list(self._library_calls)
@property
def low_level_calls(self) -> List["LowLevelCallType"]:
def low_level_calls(self) -> List["LowLevelCall"]:
"""
list((Variable|SolidityVariable, str)): List of low_level call
list(LowLevelCall): List of IR operations for low level calls
A low level call is defined by
- the variable called
- the name of the function (call/delegatecall/codecall)
- the name of the function (call/delegatecall/callcode)
"""
return list(self._low_level_calls)
@ -1121,10 +1121,14 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
values = f_new_values(self)
explored = [self]
to_explore = [
c for c in self.internal_calls if isinstance(c, Function) and c not in explored
ir.function
for ir in self.internal_calls
if isinstance(ir.function, Function) and ir.function not in explored
]
to_explore += [
c for (_, c) in self.library_calls if isinstance(c, Function) and c not in explored
ir.function
for ir in self.library_calls
if isinstance(ir.function, Function) and ir.function not in explored
]
to_explore += [m for m in self.modifiers if m not in explored]
@ -1138,14 +1142,18 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
values += f_new_values(f)
to_explore += [
c
for c in f.internal_calls
if isinstance(c, Function) and c not in explored and c not in to_explore
ir.function
for ir in f.internal_calls
if isinstance(ir.function, Function)
and ir.function not in explored
and ir.function not in to_explore
]
to_explore += [
c
for (_, c) in f.library_calls
if isinstance(c, Function) and c not in explored and c not in to_explore
ir.function
for ir in f.library_calls
if isinstance(ir.function, Function)
and ir.function not in explored
and ir.function not in to_explore
]
to_explore += [m for m in f.modifiers if m not in explored and m not in to_explore]
@ -1210,31 +1218,31 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
)
return self._all_state_variables_written
def all_internal_calls(self) -> List["InternalCallType"]:
def all_internal_calls(self) -> List["InternalCall"]:
"""recursive version of internal_calls"""
if self._all_internals_calls is None:
self._all_internals_calls = self._explore_functions(lambda x: x.internal_calls)
return self._all_internals_calls
def all_low_level_calls(self) -> List["LowLevelCallType"]:
def all_low_level_calls(self) -> List["LowLevelCall"]:
"""recursive version of low_level calls"""
if self._all_low_level_calls is None:
self._all_low_level_calls = self._explore_functions(lambda x: x.low_level_calls)
return self._all_low_level_calls
def all_high_level_calls(self) -> List["HighLevelCallType"]:
def all_high_level_calls(self) -> List[Tuple["Contract", "HighLevelCall"]]:
"""recursive version of high_level calls"""
if self._all_high_level_calls is None:
self._all_high_level_calls = self._explore_functions(lambda x: x.high_level_calls)
return self._all_high_level_calls
def all_library_calls(self) -> List["LibraryCallType"]:
def all_library_calls(self) -> List["LibraryCall"]:
"""recursive version of library calls"""
if self._all_library_calls is None:
self._all_library_calls = self._explore_functions(lambda x: x.library_calls)
return self._all_library_calls
def all_solidity_calls(self) -> List[SolidityFunction]:
def all_solidity_calls(self) -> List["SolidityCall"]:
"""recursive version of solidity calls"""
if self._all_solidity_calls is None:
self._all_solidity_calls = self._explore_functions(lambda x: x.solidity_calls)

@ -73,7 +73,8 @@ The function will return 6 bytes starting from offset 5, instead of returning a
for node in f.nodes:
if node.sons:
for function_called in node.internal_calls:
for ir in node.internal_calls:
function_called = ir.function
if isinstance(function_called, Function):
found = _assembly_node(function_called)
if found:

@ -59,7 +59,7 @@ Every Ether sent to `Locked` will be lost."""
explored += to_explore
to_explore = []
for function in functions:
calls = [c.name for c in function.internal_calls]
calls = [ir.function.name for ir in function.internal_calls]
if "suicide(address)" in calls or "selfdestruct(address)" in calls:
return False
for node in function.nodes:

@ -31,11 +31,11 @@ class ArbitrarySendErc20:
def _detect_arbitrary_from(self, contract: Contract) -> None:
for f in contract.functions:
all_high_level_calls = [
f_called[1].solidity_signature
for f_called in f.high_level_calls
if isinstance(f_called[1], Function)
ir.function.solidity_signature
for _, ir in f.high_level_calls
if isinstance(ir.function, Function)
]
all_library_calls = [f_called[1].solidity_signature for f_called in f.library_calls]
all_library_calls = [ir.function.solidity_signature for ir in f.library_calls]
if (
"transferFrom(address,address,uint256)" in all_high_level_calls
or "safeTransferFrom(address,address,address,uint256)" in all_library_calls

@ -48,13 +48,15 @@ contract Contract{
all_functionss_called = [
f.all_internal_calls() for f in contract.functions_entry_points
]
all_functions_called = [item for sublist in all_functionss_called for item in sublist]
all_functions_called = [
item.function for sublist in all_functionss_called for item in sublist
]
functions_used |= {
f.canonical_name for f in all_functions_called if isinstance(f, Function)
}
all_libss_called = [f.all_library_calls() for f in contract.functions_entry_points]
all_libs_called: List[Tuple[Contract, Function]] = [
item for sublist in all_libss_called for item in sublist
item.function for sublist in all_libss_called for item in sublist
]
functions_used |= {
lib[1].canonical_name for lib in all_libs_called if isinstance(lib, tuple)

@ -17,7 +17,7 @@ from slither.utils.output import Output
def is_revert(node: Node) -> bool:
return node.type == NodeType.THROW or any(
c.name in ["revert()", "revert(string"] for c in node.internal_calls
ir.function.name in ["revert()", "revert(string"] for ir in node.internal_calls
)

@ -101,9 +101,9 @@ Bob calls `doStuffOnL2` but the first retryable ticket calling `claim_rewards` f
# include ops from internal function calls
internal_ops = []
for internal_call in node.internal_calls:
if isinstance(internal_call, Function):
internal_ops += internal_call.all_slithir_operations()
for ir in node.internal_calls:
if isinstance(ir.function, Function):
internal_ops += ir.function.all_slithir_operations()
# analyze node for retryable tickets
for ir in node.irs + internal_ops:

@ -61,7 +61,9 @@ contract Buggy{
if not function_protection:
self.logger.error(f"{function_sig} not found")
continue
if function_protection not in function.all_internal_calls():
if function_protection not in [
ir.function for ir in function.all_internal_calls()
]:
info: DETECTOR_INFO = [
function,
" should have ",

@ -59,7 +59,7 @@ Bob calls `kill` and destructs the contract."""
if func.visibility not in ["public", "external"]:
return False
calls = [c.name for c in func.all_internal_calls()]
calls = [ir.function.name for ir in func.all_internal_calls()]
if not ("suicide(address)" in calls or "selfdestruct(address)" in calls):
return False

@ -145,15 +145,16 @@ class AbstractState:
)
slithir_operations = []
# Add the state variables written in internal calls
for internal_call in node.internal_calls:
for ir in node.internal_calls:
# Filter to Function, as internal_call can be a solidity call
if isinstance(internal_call, Function):
for internal_node in internal_call.all_nodes():
function = ir.function
if isinstance(function, Function):
for internal_node in function.all_nodes():
for read in internal_node.state_variables_read:
state_vars_read[read].add(internal_node)
for write in internal_node.state_variables_written:
state_vars_written[write].add(internal_node)
slithir_operations += internal_call.all_slithir_operations()
slithir_operations += function.all_slithir_operations()
contains_call = False

@ -32,7 +32,7 @@ def detect_assert_state_change(
for function in contract.functions_declared + list(contract.modifiers_declared):
for node in function.nodes:
# Detect assert() calls
if any(c.name == "assert(bool)" for c in node.internal_calls) and (
if any(ir.function.name == "assert(bool)" for ir in node.internal_calls) and (
# Detect direct changes to state
node.state_variables_written
or

@ -56,7 +56,7 @@ def is_assert(node: Node) -> bool:
# Old Solidity code where using an internal 'assert(bool)' function
# While we dont check that this function is correct, we assume it is
# To avoid too many FP
if "assert(bool)" in [c.full_name for c in node.internal_calls]:
if "assert(bool)" in [ir.function.full_name for ir in node.internal_calls]:
return True
return False

@ -35,8 +35,8 @@ def _has_initializing_protection(functions: List[Function]) -> bool:
for m in f.modifiers:
if m.name == "initializer":
return True
for ifc in f.all_internal_calls():
if ifc.name == "_disableInitializers":
for ir in f.all_internal_calls():
if ir.function.name == "_disableInitializers":
return True
# to avoid future FPs in different modifier + function naming implementations, we can also implement a broader check for state var "_initialized" being written to in the constructor

@ -10,6 +10,7 @@ from typing import Optional, Union, Dict, Set, Tuple, Sequence
from slither.core.declarations import Contract, FunctionContract
from slither.core.declarations.function import Function
from slither.slithir.operations import HighLevelCall, InternalCall
from slither.core.declarations.solidity_variables import SolidityFunction
from slither.core.variables.variable import Variable
from slither.printers.abstract_printer import AbstractPrinter
@ -49,26 +50,26 @@ def _node(node: str, label: Optional[str] = None) -> str:
def _process_internal_call(
contract: Contract,
function: Function,
internal_call: Union[Function, SolidityFunction],
internal_call: InternalCall,
contract_calls: Dict[Contract, Set[str]],
solidity_functions: Set[str],
solidity_calls: Set[str],
) -> None:
if isinstance(internal_call, (Function)):
if isinstance(internal_call.function, (Function)):
contract_calls[contract].add(
_edge(
_function_node(contract, function),
_function_node(contract, internal_call),
_function_node(contract, internal_call.function),
)
)
elif isinstance(internal_call, (SolidityFunction)):
elif isinstance(internal_call.function, (SolidityFunction)):
solidity_functions.add(
_node(_solidity_function_node(internal_call)),
_node(_solidity_function_node(internal_call.function)),
)
solidity_calls.add(
_edge(
_function_node(contract, function),
_solidity_function_node(internal_call),
_solidity_function_node(internal_call.function),
)
)
@ -112,29 +113,29 @@ def _render_solidity_calls(solidity_functions: Set[str], solidity_calls: Set[str
def _process_external_call(
contract: Contract,
function: Function,
external_call: Tuple[Contract, Union[Function, Variable]],
external_call: Tuple[Contract, HighLevelCall],
contract_functions: Dict[Contract, Set[str]],
external_calls: Set[str],
all_contracts: Set[Contract],
) -> None:
external_contract, external_function = external_call
external_contract, ir = external_call
if not external_contract in all_contracts:
return
# add variable as node to respective contract
if isinstance(external_function, (Variable)):
if isinstance(ir.function, (Variable)):
contract_functions[external_contract].add(
_node(
_function_node(external_contract, external_function),
external_function.name,
_function_node(external_contract, ir.function),
ir.function.name,
)
)
external_calls.add(
_edge(
_function_node(contract, function),
_function_node(external_contract, external_function),
_function_node(external_contract, ir.function),
)
)

@ -19,7 +19,11 @@ class PrinterWrittenVariablesAndAuthorization(AbstractPrinter):
@staticmethod
def get_msg_sender_checks(function: Function) -> List[str]:
all_functions = (
[f for f in function.all_internal_calls() if isinstance(f, Function)]
[
ir.function
for ir in function.all_internal_calls()
if isinstance(ir.function, Function)
]
+ [function]
+ [m for m in function.modifiers if isinstance(m, Function)]
)

@ -140,8 +140,8 @@ def _extract_assert(contracts: List[Contract]) -> Dict[str, Dict[str, List[Dict]
for contract in contracts:
functions_using_assert = [] # Dict[str, List[Dict]] = defaultdict(list)
for f in contract.functions_entry_points:
for v in f.all_solidity_calls():
if v == SolidityFunction("assert(bool)"):
for ir in f.all_solidity_calls():
if ir.function == SolidityFunction("assert(bool)"):
functions_using_assert.append(_get_name(f))
break
# Revert https://github.com/crytic/slither/pull/2105 until format is supported by echidna.

@ -29,12 +29,12 @@ class Modifiers(AbstractPrinter):
table = MyPrettyTable(["Function", "Modifiers"])
for function in contract.functions:
modifiers = function.modifiers
for call in function.all_internal_calls():
if isinstance(call, Function):
modifiers += call.modifiers
for (_, call) in function.all_library_calls():
if isinstance(call, Function):
modifiers += call.modifiers
for ir in function.all_internal_calls():
if isinstance(ir.function, Function):
modifiers += ir.function.modifiers
for ir in function.all_library_calls():
if isinstance(ir.function, Function):
modifiers += ir.function.modifiers
table.add_row([function.name, sorted([m.name for m in set(modifiers)])])
txt += "\n" + str(table)
self.info(txt)

@ -11,8 +11,8 @@ from slither.utils.myprettytable import MyPrettyTable
def _use_modifier(function: Function, modifier_name: str = "whenNotPaused") -> bool:
for internal_call in function.all_internal_calls():
if isinstance(internal_call, SolidityFunction):
for ir in function.all_internal_calls():
if isinstance(ir, function, SolidityFunction):
continue
if any(modifier.name == modifier_name for modifier in function.modifiers):
return True

@ -294,10 +294,9 @@ class Flattening:
self._export_list_used_contracts(inherited, exported, list_contract, list_top_level)
# Find all the external contracts called
externals = contract.all_library_calls + contract.all_high_level_calls
# externals is a list of (contract, function)
# High level calls already includes library calls
# We also filter call to itself to avoid infilite loop
externals = list({e[0] for e in externals if e[0] != contract})
externals = list({e[0] for e in contract.all_high_level_calls if e[0] != contract})
for inherited in externals:
self._export_list_used_contracts(inherited, exported, list_contract, list_top_level)

@ -123,10 +123,14 @@ def __find_target_paths(
# Find all function calls in this function (except for low level)
called_functions_list = [
f for (_, f) in function.high_level_calls if isinstance(f, Function)
ir.function
for _, ir in function.high_level_calls
if isinstance(ir.function, Function)
]
called_functions_list += [ir.function for ir in function.library_calls]
called_functions_list += [
ir.function for ir in function.internal_calls if isinstance(ir.function, Function)
]
called_functions_list += [f for (_, f) in function.library_calls]
called_functions_list += [f for f in function.internal_calls if isinstance(f, Function)]
called_functions = set(called_functions_list)
# If any of our target functions are reachable from this function, it's a result.

@ -123,7 +123,9 @@ def compare(
):
continue
modified_calls = [
func for func in new_modified_functions if func in function.internal_calls
func
for func in new_modified_functions
if func in [ir.function for ir in function.internal_calls]
]
tainted_vars = [
var
@ -179,7 +181,8 @@ def tainted_external_contracts(funcs: List[Function]) -> List[TaintedExternalCon
tainted_list: list[TaintedExternalContract] = []
for func in funcs:
for contract, target in func.all_high_level_calls():
for contract, ir in func.all_high_level_calls():
target = ir.function
if contract.is_library:
# Not interested in library calls
continue
@ -254,7 +257,11 @@ def tainted_inheriting_contracts(
new_taint = TaintedExternalContract(c)
for f in c.functions_declared:
# Search for functions that call an inherited tainted function or access an inherited tainted variable
internal_calls = [c for c in f.all_internal_calls() if isinstance(c, Function)]
internal_calls = [
ir.function
for ir in f.all_internal_calls()
if isinstance(ir.function, Function)
]
if any(
call.canonical_name == t.canonical_name
for t in tainted.tainted_functions

Loading…
Cancel
Save