Skip to content

Commit

Permalink
Use new *Calls API in detectors
Browse files Browse the repository at this point in the history
  • Loading branch information
smonicas committed Sep 13, 2024
1 parent 13b25e8 commit 439010a
Show file tree
Hide file tree
Showing 12 changed files with 163 additions and 192 deletions.
43 changes: 20 additions & 23 deletions slither/detectors/assembly/incorrect_return.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,8 @@ def _assembly_node(function: Function) -> Optional[SolidityCall]:
"""

for ir in function.all_slithir_operations():
if isinstance(ir, SolidityCall) and ir.function == SolidityFunction(
"return(uint256,uint256)"
):
for ir in function.all_solidity_calls():
if ir.function == SolidityFunction("return(uint256,uint256)"):
return ir
return None

Expand Down Expand Up @@ -71,24 +69,23 @@ def _detect(self) -> List[Output]:
for c in self.contracts:
for f in c.functions_and_modifiers_declared:

for node in f.nodes:
if node.sons:
for ir in node.internal_calls:
function_called = ir.function
if isinstance(function_called, Function):
found = _assembly_node(function_called)
if found:

info: DETECTOR_INFO = [
f,
" calls ",
function_called,
" which halt the execution ",
found.node,
"\n",
]
json = self.generate_result(info)

results.append(json)
for ir in f.internal_calls:
if ir.node.sons:
function_called = ir.function
if isinstance(function_called, Function):
found = _assembly_node(function_called)
if found:

info: DETECTOR_INFO = [
f,
" calls ",
function_called,
" which halt the execution ",
found.node,
"\n",
]
json = self.generate_result(info)

results.append(json)

return results
14 changes: 5 additions & 9 deletions slither/detectors/assembly/return_instead_of_leave.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
DetectorClassification,
DETECTOR_INFO,
)
from slither.slithir.operations import SolidityCall
from slither.utils.output import Output


Expand Down Expand Up @@ -42,15 +41,12 @@ class ReturnInsteadOfLeave(AbstractDetector):
def _check_function(self, f: Function) -> List[Output]:
results: List[Output] = []

for node in f.nodes:
for ir in node.irs:
if isinstance(ir, SolidityCall) and ir.function == SolidityFunction(
"return(uint256,uint256)"
):
info: DETECTOR_INFO = [f, " contains an incorrect call to return: ", node, "\n"]
json = self.generate_result(info)
for ir in f.solidity_calls:
if ir.function == SolidityFunction("return(uint256,uint256)"):
info: DETECTOR_INFO = [f, " contains an incorrect call to return: ", ir.node, "\n"]
json = self.generate_result(info)

results.append(json)
results.append(json)
return results

def _detect(self) -> List[Output]:
Expand Down
41 changes: 14 additions & 27 deletions slither/detectors/compiler_bugs/array_by_reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
from slither.core.solidity_types.array_type import ArrayType
from slither.core.variables.state_variable import StateVariable
from slither.core.variables.local_variable import LocalVariable
from slither.slithir.operations.high_level_call import HighLevelCall
from slither.slithir.operations.internal_call import InternalCall
from slither.core.cfg.node import Node
from slither.core.declarations.contract import Contract
from slither.core.declarations.function_contract import FunctionContract
Expand Down Expand Up @@ -117,37 +115,26 @@ def detect_calls_passing_ref_to_function(
# pylint: disable=too-many-nested-blocks
for contract in contracts:
for function in contract.functions_and_modifiers_declared:
for node in function.nodes:
for ir in [ir for _, ir in function.high_level_calls] + function.internal_calls:

# If this node has no expression, skip it.
if not node.expression:
# Verify this references a function in our array modifying functions collection.
if ir.function not in array_modifying_funcs:
continue

for ir in node.irs:
# Verify this is a high level call.
if not isinstance(ir, (HighLevelCall, InternalCall)):
# Verify one of these parameters is an array in storage.
for (param, arg) in zip(ir.function.parameters, ir.arguments):
# Verify this argument is a variable that is an array type.
if not isinstance(arg, (StateVariable, LocalVariable)):
continue

# Verify this references a function in our array modifying functions collection.
if ir.function not in array_modifying_funcs:
if not isinstance(arg.type, ArrayType):
continue

# Verify one of these parameters is an array in storage.
for (param, arg) in zip(ir.function.parameters, ir.arguments):
# Verify this argument is a variable that is an array type.
if not isinstance(arg, (StateVariable, LocalVariable)):
continue
if not isinstance(arg.type, ArrayType):
continue

# If it is a state variable OR a local variable referencing storage, we add it to the list.
if (
isinstance(arg, StateVariable)
or (isinstance(arg, LocalVariable) and arg.location == "storage")
) and (
isinstance(param.type, ArrayType) and param.location != "storage"
):
results.append((node, arg, ir.function))
# If it is a state variable OR a local variable referencing storage, we add it to the list.
if (
isinstance(arg, StateVariable)
or (isinstance(arg, LocalVariable) and arg.location == "storage")
) and (isinstance(param.type, ArrayType) and param.location != "storage"):
results.append((ir.node, arg, ir.function))
return results

def _detect(self) -> List[Output]:
Expand Down
79 changes: 39 additions & 40 deletions slither/detectors/erc/erc20/arbitrary_send_erc20.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from slither.analyses.data_dependency.data_dependency import is_dependent
from slither.core.cfg.node import Node
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.declarations import Contract, Function, SolidityVariableComposed
from slither.core.declarations import Contract, Function, SolidityVariableComposed, FunctionContract
from slither.core.declarations.solidity_variables import SolidityVariable
from slither.slithir.operations import HighLevelCall, LibraryCall

Expand Down Expand Up @@ -44,51 +44,50 @@ def _detect_arbitrary_from(self, contract: Contract) -> None:
"permit(address,address,uint256,uint256,uint8,bytes32,bytes32)"
in all_high_level_calls
):
ArbitrarySendErc20._arbitrary_from(f.nodes, self._permit_results)
ArbitrarySendErc20._arbitrary_from(f, self._permit_results)
else:
ArbitrarySendErc20._arbitrary_from(f.nodes, self._no_permit_results)
ArbitrarySendErc20._arbitrary_from(f, self._no_permit_results)

@staticmethod
def _arbitrary_from(nodes: List[Node], results: List[Node]) -> None:
def _arbitrary_from(function: FunctionContract, results: List[Node]) -> None:
"""Finds instances of (safe)transferFrom that do not use msg.sender or address(this) as from parameter."""
for node in nodes:
for ir in node.irs:
if (
isinstance(ir, HighLevelCall)
and isinstance(ir.function, Function)
and ir.function.solidity_signature == "transferFrom(address,address,uint256)"
and not (
is_dependent(
ir.arguments[0],
SolidityVariableComposed("msg.sender"),
node,
)
or is_dependent(
ir.arguments[0],
SolidityVariable("this"),
node,
)
for _, ir in function.high_level_calls:
if (
isinstance(ir, LibraryCall)
and ir.function.solidity_signature
== "safeTransferFrom(address,address,address,uint256)"
and not (
is_dependent(
ir.arguments[1],
SolidityVariableComposed("msg.sender"),
ir.node,
)
):
results.append(ir.node)
elif (
isinstance(ir, LibraryCall)
and ir.function.solidity_signature
== "safeTransferFrom(address,address,address,uint256)"
and not (
is_dependent(
ir.arguments[1],
SolidityVariableComposed("msg.sender"),
node,
)
or is_dependent(
ir.arguments[1],
SolidityVariable("this"),
node,
)
or is_dependent(
ir.arguments[1],
SolidityVariable("this"),
ir.node,
)
):
results.append(ir.node)
)
):
results.append(ir.node)
elif (
isinstance(ir, HighLevelCall)
and isinstance(ir.function, Function)
and ir.function.solidity_signature == "transferFrom(address,address,uint256)"
and not (
is_dependent(
ir.arguments[0],
SolidityVariableComposed("msg.sender"),
ir.node,
)
or is_dependent(
ir.arguments[0],
SolidityVariable("this"),
ir.node,
)
)
):
results.append(ir.node)

def detect(self) -> None:
"""Detect transfers that use arbitrary `from` parameter."""
Expand Down
13 changes: 6 additions & 7 deletions slither/detectors/functions/external_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
make_solc_versions,
)
from slither.formatters.functions.external_function import custom_format
from slither.slithir.operations import InternalCall, InternalDynamicCall
from slither.slithir.operations import SolidityCall
from slither.slithir.operations import InternalDynamicCall
from slither.utils.output import Output


Expand Down Expand Up @@ -55,11 +54,11 @@ def detect_functions_called(contract: Contract) -> List[Function]:
for func in contract.all_functions_called:
if not isinstance(func, Function):
continue
# Loop through all nodes in the function, add all calls to a list.
for node in func.nodes:
for ir in node.irs:
if isinstance(ir, (InternalCall, SolidityCall)):
result.append(ir.function)

# Loop through all internal and solidity calls in the function, add them to a list.
for ir in func.internal_calls + func.solidity_calls:
result.append(ir.function)

return result

@staticmethod
Expand Down
37 changes: 17 additions & 20 deletions slither/detectors/operations/encode_packed.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
"""

from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.core.declarations.solidity_variables import SolidityFunction
from slither.slithir.operations import SolidityCall
from slither.core.declarations import Contract, SolidityFunction
from slither.core.variables import Variable
from slither.analyses.data_dependency.data_dependency import is_tainted
from slither.core.solidity_types import ElementaryType
from slither.core.solidity_types import ArrayType


def _is_dynamic_type(arg):
def _is_dynamic_type(arg: Variable):
"""
Args:
arg (function argument)
Expand All @@ -25,7 +25,7 @@ def _is_dynamic_type(arg):
return False


def _detect_abi_encodePacked_collision(contract):
def _detect_abi_encodePacked_collision(contract: Contract):
"""
Args:
contract (Contract)
Expand All @@ -35,22 +35,19 @@ def _detect_abi_encodePacked_collision(contract):
ret = []
# pylint: disable=too-many-nested-blocks
for f in contract.functions_and_modifiers_declared:
for n in f.nodes:
for ir in n.irs:
if isinstance(ir, SolidityCall) and ir.function == SolidityFunction(
"abi.encodePacked()"
):
dynamic_type_count = 0
for arg in ir.arguments:
if is_tainted(arg, contract) and _is_dynamic_type(arg):
dynamic_type_count += 1
elif dynamic_type_count > 1:
ret.append((f, n))
dynamic_type_count = 0
else:
dynamic_type_count = 0
if dynamic_type_count > 1:
ret.append((f, n))
for ir in f.solidity_calls:
if ir.function == SolidityFunction("abi.encodePacked()"):
dynamic_type_count = 0
for arg in ir.arguments:
if is_tainted(arg, contract) and _is_dynamic_type(arg):
dynamic_type_count += 1
elif dynamic_type_count > 1:
ret.append((f, ir.node))
dynamic_type_count = 0
else:
dynamic_type_count = 0
if dynamic_type_count > 1:
ret.append((f, ir.node))
return ret


Expand Down
7 changes: 3 additions & 4 deletions slither/detectors/operations/low_level_calls.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,9 @@ def detect_low_level_calls(
) -> List[Tuple[FunctionContract, List[Node]]]:
ret = []
for f in [f for f in contract.functions if contract == f.contract_declarer]:
nodes = f.nodes
assembly_nodes = [n for n in nodes if self._contains_low_level_calls(n)]
if assembly_nodes:
ret.append((f, assembly_nodes))
low_level_nodes = [ir.node for ir in f.low_level_calls]
if low_level_nodes:
ret.append((f, low_level_nodes))
return ret

def _detect(self) -> List[Output]:
Expand Down
10 changes: 5 additions & 5 deletions slither/detectors/statements/assert_state_change.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,22 @@ def detect_assert_state_change(

# Loop for each function and modifier.
for function in contract.functions_declared + list(contract.modifiers_declared):
for node in function.nodes:
for ir_call in function.internal_calls:
# Detect assert() calls
if any(ir.function.name == "assert(bool)" for ir in node.internal_calls) and (
if ir_call.function.name == "assert(bool)" and (
# Detect direct changes to state
node.state_variables_written
ir_call.node.state_variables_written
or
# Detect changes to state via function calls
any(
ir
for ir in node.irs
for ir in ir_call.node.irs
if isinstance(ir, InternalCall)
and ir.function
and ir.function.state_variables_written
)
):
results.append((function, node))
results.append((function, ir_call.node))

# Return the resulting set of nodes
return results
Expand Down
Loading

0 comments on commit 439010a

Please sign in to comment.