Update the cross function reentrancy heuristic to work on variable read, and public state variables

pull/1351/head
Josselin Feist 2 years ago
parent 303ed83736
commit d2a93f0225
  1. 33
      slither/core/declarations/contract.py
  2. 24
      slither/detectors/reentrancy/reentrancy_eth.py
  3. 24
      slither/detectors/reentrancy/reentrancy_read_before_write.py
  4. 45
      tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol
  5. 677
      tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol.0.8.10.ReentrancyEth.json

@ -2,8 +2,9 @@
Contract module Contract module
""" """
import logging import logging
from collections import defaultdict
from pathlib import Path from pathlib import Path
from typing import Optional, List, Dict, Callable, Tuple, TYPE_CHECKING, Union from typing import Optional, List, Dict, Callable, Tuple, TYPE_CHECKING, Union, Set
from crytic_compile.platform import Type as PlatformType from crytic_compile.platform import Type as PlatformType
@ -101,7 +102,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
self.file_scope: "FileScope" = scope self.file_scope: "FileScope" = scope
# memoize # memoize
self._state_variables_written_in_reentrant_targets: Optional[List["StateVariable"]] = None self._state_variables_used_in_reentrant_targets: Optional[Dict["StateVariable", Set[Union["StateVariable", "Function"]]]]= None
################################################################################### ###################################################################################
################################################################################### ###################################################################################
@ -353,14 +354,26 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
return list(set(slithir_variables)) return list(set(slithir_variables))
@property @property
def state_variables_written_in_reentrant_targets(self) -> List["StateVariable"]: def state_variables_used_in_reentrant_targets(self) -> Dict["StateVariable", Set[Union["StateVariable", "Function"]]]:
if self._state_variables_written_in_reentrant_targets is None: """
reentrant_functions = [f for f in self.functions if f.is_reentrant] Returns the state variables used in reentrant targets. Heuristics:
variables_writtenss = [f.all_state_variables_written() for f in reentrant_functions] - Variable used (read/write) in entry points that are reentrant
self._state_variables_written_in_reentrant_targets = [ - State variables that are public
item for sublist in variables_writtenss for item in sublist
] """
return self._state_variables_written_in_reentrant_targets from slither.core.variables.state_variable import StateVariable
if self._state_variables_used_in_reentrant_targets is None:
reentrant_functions = [f for f in self.functions_entry_points if f.is_reentrant]
variables_used: Dict[StateVariable, Set[Union[StateVariable, "Function"]]] = defaultdict(set)
for function in reentrant_functions:
for ir in function.all_slithir_operations():
state_variables = [v for v in ir.used if isinstance(v, StateVariable)]
for state_variable in state_variables:
variables_used[state_variable].add(ir.function)
for variable in [v for v in self.state_variables if v.visibility == "public"]:
variables_used[variable].add(variable)
self._state_variables_used_in_reentrant_targets = variables_used
return self._state_variables_used_in_reentrant_targets
# endregion # endregion
################################################################################### ###################################################################################

@ -5,13 +5,14 @@
Iterate over all the nodes of the graph until reaching a fixpoint Iterate over all the nodes of the graph until reaching a fixpoint
""" """
from collections import namedtuple, defaultdict from collections import namedtuple, defaultdict
from typing import List from typing import List, Dict, Set
from slither.detectors.abstract_detector import DetectorClassification from slither.detectors.abstract_detector import DetectorClassification
from .reentrancy import Reentrancy, to_hashable from .reentrancy import Reentrancy, to_hashable
from ...utils.output import Output
FindingKey = namedtuple("FindingKey", ["function", "calls", "send_eth"]) FindingKey = namedtuple("FindingKey", ["function", "calls", "send_eth"])
FindingValue = namedtuple("FindingValue", ["variable", "node", "nodes"]) FindingValue = namedtuple("FindingValue", ["variable", "node", "nodes", "cross_functions"])
class ReentrancyEth(Reentrancy): class ReentrancyEth(Reentrancy):
@ -52,9 +53,10 @@ Bob uses the re-entrancy bug to call `withdrawBalance` two times, and withdraw m
STANDARD_JSON = False STANDARD_JSON = False
def find_reentrancies(self): def find_reentrancies(self) -> Dict[FindingKey, Set[FindingValue]]:
result = defaultdict(set) result: Dict[FindingKey, Set[FindingValue]] = defaultdict(set)
for contract in self.contracts: # pylint: disable=too-many-nested-blocks for contract in self.contracts: # pylint: disable=too-many-nested-blocks
variables_used_in_reentrancy = contract.state_variables_used_in_reentrant_targets
for f in contract.functions_and_modifiers_declared: for f in contract.functions_and_modifiers_declared:
for node in f.nodes: for node in f.nodes:
# dead code # dead code
@ -72,12 +74,13 @@ Bob uses the re-entrancy bug to call `withdrawBalance` two times, and withdraw m
v, v,
node, node,
tuple(sorted(nodes, key=lambda x: x.node_id)), tuple(sorted(nodes, key=lambda x: x.node_id)),
tuple(variables_used_in_reentrancy[v])
) )
for (v, nodes) in node.context[self.KEY].written.items() for (v, nodes) in node.context[self.KEY].written.items()
if v in node.context[self.KEY].reads_prior_calls[c] if v in node.context[self.KEY].reads_prior_calls[c]
and ( and (
f.is_reentrant f.is_reentrant
or v in contract.state_variables_written_in_reentrant_targets or v in variables_used_in_reentrancy
) )
} }
@ -92,7 +95,7 @@ Bob uses the re-entrancy bug to call `withdrawBalance` two times, and withdraw m
result[finding_key] |= set(read_then_written) result[finding_key] |= set(read_then_written)
return result return result
def _detect(self): # pylint: disable=too-many-branches def _detect(self) -> List[Output]: # pylint: disable=too-many-branches,too-many-locals
"""""" """"""
super()._detect() super()._detect()
@ -102,10 +105,11 @@ Bob uses the re-entrancy bug to call `withdrawBalance` two times, and withdraw m
result_sorted = sorted(list(reentrancies.items()), key=lambda x: x[0].function.name) result_sorted = sorted(list(reentrancies.items()), key=lambda x: x[0].function.name)
varsWritten: List[FindingValue] varsWritten: List[FindingValue]
for (func, calls, send_eth), varsWritten in result_sorted: varsWrittenSet: Set[FindingValue]
for (func, calls, send_eth), varsWrittenSet in result_sorted:
calls = sorted(list(set(calls)), key=lambda x: x[0].node_id) calls = sorted(list(set(calls)), key=lambda x: x[0].node_id)
send_eth = sorted(list(set(send_eth)), key=lambda x: x[0].node_id) send_eth = sorted(list(set(send_eth)), key=lambda x: x[0].node_id)
varsWritten = sorted(varsWritten, key=lambda x: (x.variable.name, x.node.node_id)) varsWritten = sorted(varsWrittenSet, key=lambda x: (x.variable.name, x.node.node_id))
info = ["Reentrancy in ", func, ":\n"] info = ["Reentrancy in ", func, ":\n"]
info += ["\tExternal calls:\n"] info += ["\tExternal calls:\n"]
@ -127,6 +131,10 @@ Bob uses the re-entrancy bug to call `withdrawBalance` two times, and withdraw m
for other_node in finding_value.nodes: for other_node in finding_value.nodes:
if other_node != finding_value.node: if other_node != finding_value.node:
info += ["\t\t- ", other_node, "\n"] info += ["\t\t- ", other_node, "\n"]
if finding_value.cross_functions:
info += ["\t", finding_value.variable," can be used in cross function reentrancies:\n"]
for cross in finding_value.cross_functions:
info += ["\t- ", cross, "\n"]
# Create our JSON result # Create our JSON result
res = self.generate_result(info) res = self.generate_result(info)

@ -5,12 +5,14 @@
Iterate over all the nodes of the graph until reaching a fixpoint Iterate over all the nodes of the graph until reaching a fixpoint
""" """
from collections import namedtuple, defaultdict from collections import namedtuple, defaultdict
from typing import Dict, Set, List
from slither.detectors.abstract_detector import DetectorClassification from slither.detectors.abstract_detector import DetectorClassification
from .reentrancy import Reentrancy, to_hashable from .reentrancy import Reentrancy, to_hashable
from ...utils.output import Output
FindingKey = namedtuple("FindingKey", ["function", "calls"]) FindingKey = namedtuple("FindingKey", ["function", "calls"])
FindingValue = namedtuple("FindingValue", ["variable", "node", "nodes"]) FindingValue = namedtuple("FindingValue", ["variable", "node", "nodes", "cross_functions"])
class ReentrancyReadBeforeWritten(Reentrancy): class ReentrancyReadBeforeWritten(Reentrancy):
@ -49,9 +51,10 @@ Do not report reentrancies that involve Ether (see `reentrancy-eth`)."""
STANDARD_JSON = False STANDARD_JSON = False
def find_reentrancies(self): def find_reentrancies(self) -> Dict[FindingKey, Set[FindingValue]]:
result = defaultdict(set) result: Dict[FindingKey, Set[FindingValue]] = defaultdict(set)
for contract in self.contracts: # pylint: disable=too-many-nested-blocks for contract in self.contracts: # pylint: disable=too-many-nested-blocks
variables_used_in_reentrancy = contract.state_variables_used_in_reentrant_targets
for f in contract.functions_and_modifiers_declared: for f in contract.functions_and_modifiers_declared:
for node in f.nodes: for node in f.nodes:
# dead code # dead code
@ -67,12 +70,13 @@ Do not report reentrancies that involve Ether (see `reentrancy-eth`)."""
v, v,
node, node,
tuple(sorted(nodes, key=lambda x: x.node_id)), tuple(sorted(nodes, key=lambda x: x.node_id)),
tuple(variables_used_in_reentrancy[v])
) )
for (v, nodes) in node.context[self.KEY].written.items() for (v, nodes) in node.context[self.KEY].written.items()
if v in node.context[self.KEY].reads_prior_calls[c] if v in node.context[self.KEY].reads_prior_calls[c]
and ( and (
f.is_reentrant f.is_reentrant
or v in contract.state_variables_written_in_reentrant_targets or v in variables_used_in_reentrancy
) )
} }
@ -86,7 +90,7 @@ Do not report reentrancies that involve Ether (see `reentrancy-eth`)."""
result[finding_key] |= read_then_written result[finding_key] |= read_then_written
return result return result
def _detect(self): # pylint: disable=too-many-branches def _detect(self) -> List[Output]: # pylint: disable=too-many-branches
"""""" """"""
super()._detect() super()._detect()
@ -95,9 +99,11 @@ Do not report reentrancies that involve Ether (see `reentrancy-eth`)."""
results = [] results = []
result_sorted = sorted(list(reentrancies.items()), key=lambda x: x[0].function.name) result_sorted = sorted(list(reentrancies.items()), key=lambda x: x[0].function.name)
for (func, calls), varsWritten in result_sorted: varsWritten: List[FindingValue]
varsWrittenSet: Set[FindingValue]
for (func, calls), varsWrittenSet in result_sorted:
calls = sorted(list(set(calls)), key=lambda x: x[0].node_id) calls = sorted(list(set(calls)), key=lambda x: x[0].node_id)
varsWritten = sorted(varsWritten, key=lambda x: (x.variable.name, x.node.node_id)) varsWritten = sorted(varsWrittenSet, key=lambda x: (x.variable.name, x.node.node_id))
info = ["Reentrancy in ", func, ":\n"] info = ["Reentrancy in ", func, ":\n"]
@ -113,6 +119,10 @@ Do not report reentrancies that involve Ether (see `reentrancy-eth`)."""
for other_node in finding_value.nodes: for other_node in finding_value.nodes:
if other_node != finding_value.node: if other_node != finding_value.node:
info += ["\t\t- ", other_node, "\n"] info += ["\t\t- ", other_node, "\n"]
if finding_value.cross_functions:
info += ["\t", finding_value.variable," can be used in cross function reentrancies:\n"]
for cross in finding_value.cross_functions:
info += ["\t- ", cross, "\n"]
# Create our JSON result # Create our JSON result
res = self.generate_result(info) res = self.generate_result(info)

@ -105,4 +105,47 @@ contract TestWithoutBugInternal{
Receiver(msg.sender).send_funds{value: amount}(); Receiver(msg.sender).send_funds{value: amount}();
} }
} }
contract TestBugWithPublicVariable{
mapping(address => uint) public balances;
modifier nonReentrant(){
_;
}
function withdraw(uint amount) nonReentrant public{
withdraw_internal(amount);
}
function withdraw_internal(uint amount) internal{
require(amount <= balances[msg.sender]);
Receiver(msg.sender).send_funds{value: amount}();
balances[msg.sender] -= amount;
}
}
contract TestWithBugNonReentrantRead{
mapping(address => uint) balances;
modifier nonReentrant(){
_;
}
function withdraw(uint amount) nonReentrant public{
require(amount <= balances[msg.sender]);
Receiver(msg.sender).send_funds{value: amount}();
balances[msg.sender] -= amount;
}
// Simulate a reentrancy that allows to read variable in a potential incorrect state during a reentrancy
// This is more likely to impact protocol like reentrancy
function read() public returns(uint){
uint amount = balances[msg.sender];
return amount;
}
}

@ -6,18 +6,18 @@
"type": "function", "type": "function",
"name": "withdraw", "name": "withdraw",
"source_mapping": { "source_mapping": {
"start": 181, "start": 3089,
"length": 207, "length": 207,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol", "filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH", "filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol", "filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false, "is_dependency": false,
"lines": [ "lines": [
13, 138,
14, 139,
15, 140,
16, 141,
17 142
], ],
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
@ -25,36 +25,37 @@
"type_specific_fields": { "type_specific_fields": {
"parent": { "parent": {
"type": "contract", "type": "contract",
"name": "TestWithBug", "name": "TestWithBugNonReentrantRead",
"source_mapping": { "source_mapping": {
"start": 67, "start": 2959,
"length": 506, "length": 629,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol", "filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH", "filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol", "filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false, "is_dependency": false,
"lines": [ "lines": [
5, 130,
6, 131,
7, 132,
8, 133,
9, 134,
10, 135,
11, 136,
12, 137,
13, 138,
14, 139,
15, 140,
16, 141,
17, 142,
18, 143,
19, 144,
20, 145,
21, 146,
22, 147,
23, 148,
24, 149,
25 150,
151
], ],
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
@ -67,14 +68,14 @@
"type": "node", "type": "node",
"name": "Receiver(msg.sender).send_funds{value: amount}()", "name": "Receiver(msg.sender).send_funds{value: amount}()",
"source_mapping": { "source_mapping": {
"start": 292, "start": 3200,
"length": 48, "length": 48,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol", "filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH", "filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol", "filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false, "is_dependency": false,
"lines": [ "lines": [
15 140
], ],
"starting_column": 10, "starting_column": 10,
"ending_column": 58 "ending_column": 58
@ -84,18 +85,18 @@
"type": "function", "type": "function",
"name": "withdraw", "name": "withdraw",
"source_mapping": { "source_mapping": {
"start": 181, "start": 3089,
"length": 207, "length": 207,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol", "filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH", "filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol", "filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false, "is_dependency": false,
"lines": [ "lines": [
13, 138,
14, 139,
15, 140,
16, 141,
17 142
], ],
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
@ -103,36 +104,37 @@
"type_specific_fields": { "type_specific_fields": {
"parent": { "parent": {
"type": "contract", "type": "contract",
"name": "TestWithBug", "name": "TestWithBugNonReentrantRead",
"source_mapping": { "source_mapping": {
"start": 67, "start": 2959,
"length": 506, "length": 629,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol", "filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH", "filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol", "filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false, "is_dependency": false,
"lines": [ "lines": [
5, 130,
6, 131,
7, 132,
8, 133,
9, 134,
10, 135,
11, 136,
12, 137,
13, 138,
14, 139,
15, 140,
16, 141,
17, 142,
18, 143,
19, 144,
20, 145,
21, 146,
22, 147,
23, 148,
24, 149,
25 150,
151
], ],
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
@ -150,14 +152,14 @@
"type": "node", "type": "node",
"name": "balances[msg.sender] -= amount", "name": "balances[msg.sender] -= amount",
"source_mapping": { "source_mapping": {
"start": 351, "start": 3259,
"length": 30, "length": 30,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol", "filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH", "filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol", "filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false, "is_dependency": false,
"lines": [ "lines": [
16 141
], ],
"starting_column": 10, "starting_column": 10,
"ending_column": 40 "ending_column": 40
@ -167,18 +169,18 @@
"type": "function", "type": "function",
"name": "withdraw", "name": "withdraw",
"source_mapping": { "source_mapping": {
"start": 181, "start": 3089,
"length": 207, "length": 207,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol", "filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH", "filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol", "filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false, "is_dependency": false,
"lines": [ "lines": [
13, 138,
14, 139,
15, 140,
16, 141,
17 142
], ],
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
@ -186,36 +188,37 @@
"type_specific_fields": { "type_specific_fields": {
"parent": { "parent": {
"type": "contract", "type": "contract",
"name": "TestWithBug", "name": "TestWithBugNonReentrantRead",
"source_mapping": { "source_mapping": {
"start": 67, "start": 2959,
"length": 506, "length": 629,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol", "filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH", "filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol", "filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false, "is_dependency": false,
"lines": [ "lines": [
5, 130,
6, 131,
7, 132,
8, 133,
9, 134,
10, 135,
11, 136,
12, 137,
13, 138,
14, 139,
15, 140,
16, 141,
17, 142,
18, 143,
19, 144,
20, 145,
21, 146,
22, 147,
23, 148,
24, 149,
25 150,
151
], ],
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
@ -231,10 +234,10 @@
} }
} }
], ],
"description": "Reentrancy in TestWithBug.withdraw(uint256) (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#13-17):\n\tExternal calls:\n\t- Receiver(msg.sender).send_funds{value: amount}() (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#15)\n\tState variables written after the call(s):\n\t- balances[msg.sender] -= amount (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#16)\n", "description": "Reentrancy in TestWithBugNonReentrantRead.withdraw(uint256) (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#138-142):\n\tExternal calls:\n\t- Receiver(msg.sender).send_funds{value: amount}() (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#140)\n\tState variables written after the call(s):\n\t- balances[msg.sender] -= amount (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#141)\n\tTestWithBugNonReentrantRead.balances (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#132) can be used in cross function reentrancies:\n\t- TestWithBugNonReentrantRead.read() (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#146-149)\n",
"markdown": "Reentrancy in [TestWithBug.withdraw(uint256)](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L13-L17):\n\tExternal calls:\n\t- [Receiver(msg.sender).send_funds{value: amount}()](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L15)\n\tState variables written after the call(s):\n\t- [balances[msg.sender] -= amount](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L16)\n", "markdown": "Reentrancy in [TestWithBugNonReentrantRead.withdraw(uint256)](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L138-L142):\n\tExternal calls:\n\t- [Receiver(msg.sender).send_funds{value: amount}()](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L140)\n\tState variables written after the call(s):\n\t- [balances[msg.sender] -= amount](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L141)\n\t[TestWithBugNonReentrantRead.balances](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L132) can be used in cross function reentrancies:\n\t- [TestWithBugNonReentrantRead.read()](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L146-L149)\n",
"first_markdown_element": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L13-L17", "first_markdown_element": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L138-L142",
"id": "746b848eb5d1102128ce24fa085ea13e78073daa2e2934383a204dfdcf8f02c0", "id": "0b2149d8ea8554c24092bad5ce3061d661d4f0447d5d96716893538474bca40f",
"check": "reentrancy-eth", "check": "reentrancy-eth",
"impact": "High", "impact": "High",
"confidence": "Medium" "confidence": "Medium"
@ -494,10 +497,482 @@
} }
} }
], ],
"description": "Reentrancy in TestWithBugInternal.withdraw_internal(uint256) (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#62-66):\n\tExternal calls:\n\t- Receiver(msg.sender).send_funds{value: amount}() (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#64)\n\tState variables written after the call(s):\n\t- balances[msg.sender] -= amount (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#65)\n", "description": "Reentrancy in TestWithBugInternal.withdraw_internal(uint256) (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#62-66):\n\tExternal calls:\n\t- Receiver(msg.sender).send_funds{value: amount}() (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#64)\n\tState variables written after the call(s):\n\t- balances[msg.sender] -= amount (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#65)\n\tTestWithBugInternal.balances (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#52) can be used in cross function reentrancies:\n\t- TestWithBugInternal.withdraw_all_internal() (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#72-76)\n",
"markdown": "Reentrancy in [TestWithBugInternal.withdraw_internal(uint256)](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L62-L66):\n\tExternal calls:\n\t- [Receiver(msg.sender).send_funds{value: amount}()](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L64)\n\tState variables written after the call(s):\n\t- [balances[msg.sender] -= amount](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L65)\n", "markdown": "Reentrancy in [TestWithBugInternal.withdraw_internal(uint256)](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L62-L66):\n\tExternal calls:\n\t- [Receiver(msg.sender).send_funds{value: amount}()](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L64)\n\tState variables written after the call(s):\n\t- [balances[msg.sender] -= amount](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L65)\n\t[TestWithBugInternal.balances](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L52) can be used in cross function reentrancies:\n\t- [TestWithBugInternal.withdraw_all_internal()](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L72-L76)\n",
"first_markdown_element": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L62-L66", "first_markdown_element": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L62-L66",
"id": "d0680f2465dbe3b98cba27b175607d789949a9d1a1b686bd373b584caf3a0913", "id": "7d618f027540d61d9af79a3a9475677476d1c4d7ad1be68ff8026f6c0d4cdc82",
"check": "reentrancy-eth",
"impact": "High",
"confidence": "Medium"
},
{
"elements": [
{
"type": "function",
"name": "withdraw_internal",
"source_mapping": {
"start": 2749,
"length": 205,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false,
"lines": [
122,
123,
124,
125,
126
],
"starting_column": 5,
"ending_column": 6
},
"type_specific_fields": {
"parent": {
"type": "contract",
"name": "TestBugWithPublicVariable",
"source_mapping": {
"start": 2516,
"length": 441,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false,
"lines": [
110,
111,
112,
113,
114,
115,
116,
117,
118,
119,
120,
121,
122,
123,
124,
125,
126,
127,
128
],
"starting_column": 1,
"ending_column": 2
}
},
"signature": "withdraw_internal(uint256)"
}
},
{
"type": "node",
"name": "Receiver(msg.sender).send_funds{value: amount}()",
"source_mapping": {
"start": 2858,
"length": 48,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false,
"lines": [
124
],
"starting_column": 10,
"ending_column": 58
},
"type_specific_fields": {
"parent": {
"type": "function",
"name": "withdraw_internal",
"source_mapping": {
"start": 2749,
"length": 205,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false,
"lines": [
122,
123,
124,
125,
126
],
"starting_column": 5,
"ending_column": 6
},
"type_specific_fields": {
"parent": {
"type": "contract",
"name": "TestBugWithPublicVariable",
"source_mapping": {
"start": 2516,
"length": 441,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false,
"lines": [
110,
111,
112,
113,
114,
115,
116,
117,
118,
119,
120,
121,
122,
123,
124,
125,
126,
127,
128
],
"starting_column": 1,
"ending_column": 2
}
},
"signature": "withdraw_internal(uint256)"
}
}
},
"additional_fields": {
"underlying_type": "external_calls"
}
},
{
"type": "node",
"name": "balances[msg.sender] -= amount",
"source_mapping": {
"start": 2917,
"length": 30,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false,
"lines": [
125
],
"starting_column": 10,
"ending_column": 40
},
"type_specific_fields": {
"parent": {
"type": "function",
"name": "withdraw_internal",
"source_mapping": {
"start": 2749,
"length": 205,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false,
"lines": [
122,
123,
124,
125,
126
],
"starting_column": 5,
"ending_column": 6
},
"type_specific_fields": {
"parent": {
"type": "contract",
"name": "TestBugWithPublicVariable",
"source_mapping": {
"start": 2516,
"length": 441,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false,
"lines": [
110,
111,
112,
113,
114,
115,
116,
117,
118,
119,
120,
121,
122,
123,
124,
125,
126,
127,
128
],
"starting_column": 1,
"ending_column": 2
}
},
"signature": "withdraw_internal(uint256)"
}
}
},
"additional_fields": {
"underlying_type": "variables_written",
"variable_name": "balances"
}
}
],
"description": "Reentrancy in TestBugWithPublicVariable.withdraw_internal(uint256) (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#122-126):\n\tExternal calls:\n\t- Receiver(msg.sender).send_funds{value: amount}() (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#124)\n\tState variables written after the call(s):\n\t- balances[msg.sender] -= amount (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#125)\n\tTestBugWithPublicVariable.balances (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#112) can be used in cross function reentrancies:\n\t- TestBugWithPublicVariable.balances (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#112)\n",
"markdown": "Reentrancy in [TestBugWithPublicVariable.withdraw_internal(uint256)](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L122-L126):\n\tExternal calls:\n\t- [Receiver(msg.sender).send_funds{value: amount}()](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L124)\n\tState variables written after the call(s):\n\t- [balances[msg.sender] -= amount](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L125)\n\t[TestBugWithPublicVariable.balances](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L112) can be used in cross function reentrancies:\n\t- [TestBugWithPublicVariable.balances](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L112)\n",
"first_markdown_element": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L122-L126",
"id": "a3e52c882aa9fb88119aa3507f4158436bfe3f1abee0828665afa41213587097",
"check": "reentrancy-eth",
"impact": "High",
"confidence": "Medium"
},
{
"elements": [
{
"type": "function",
"name": "withdraw",
"source_mapping": {
"start": 181,
"length": 207,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false,
"lines": [
13,
14,
15,
16,
17
],
"starting_column": 5,
"ending_column": 6
},
"type_specific_fields": {
"parent": {
"type": "contract",
"name": "TestWithBug",
"source_mapping": {
"start": 67,
"length": 506,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false,
"lines": [
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25
],
"starting_column": 1,
"ending_column": 2
}
},
"signature": "withdraw(uint256)"
}
},
{
"type": "node",
"name": "Receiver(msg.sender).send_funds{value: amount}()",
"source_mapping": {
"start": 292,
"length": 48,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false,
"lines": [
15
],
"starting_column": 10,
"ending_column": 58
},
"type_specific_fields": {
"parent": {
"type": "function",
"name": "withdraw",
"source_mapping": {
"start": 181,
"length": 207,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false,
"lines": [
13,
14,
15,
16,
17
],
"starting_column": 5,
"ending_column": 6
},
"type_specific_fields": {
"parent": {
"type": "contract",
"name": "TestWithBug",
"source_mapping": {
"start": 67,
"length": 506,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false,
"lines": [
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25
],
"starting_column": 1,
"ending_column": 2
}
},
"signature": "withdraw(uint256)"
}
}
},
"additional_fields": {
"underlying_type": "external_calls"
}
},
{
"type": "node",
"name": "balances[msg.sender] -= amount",
"source_mapping": {
"start": 351,
"length": 30,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false,
"lines": [
16
],
"starting_column": 10,
"ending_column": 40
},
"type_specific_fields": {
"parent": {
"type": "function",
"name": "withdraw",
"source_mapping": {
"start": 181,
"length": 207,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false,
"lines": [
13,
14,
15,
16,
17
],
"starting_column": 5,
"ending_column": 6
},
"type_specific_fields": {
"parent": {
"type": "contract",
"name": "TestWithBug",
"source_mapping": {
"start": 67,
"length": 506,
"filename_relative": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol",
"is_dependency": false,
"lines": [
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25
],
"starting_column": 1,
"ending_column": 2
}
},
"signature": "withdraw(uint256)"
}
}
},
"additional_fields": {
"underlying_type": "variables_written",
"variable_name": "balances"
}
}
],
"description": "Reentrancy in TestWithBug.withdraw(uint256) (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#13-17):\n\tExternal calls:\n\t- Receiver(msg.sender).send_funds{value: amount}() (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#15)\n\tState variables written after the call(s):\n\t- balances[msg.sender] -= amount (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#16)\n\tTestWithBug.balances (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#7) can be used in cross function reentrancies:\n\t- TestWithBug.withdraw_all() (tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#19-23)\n",
"markdown": "Reentrancy in [TestWithBug.withdraw(uint256)](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L13-L17):\n\tExternal calls:\n\t- [Receiver(msg.sender).send_funds{value: amount}()](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L15)\n\tState variables written after the call(s):\n\t- [balances[msg.sender] -= amount](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L16)\n\t[TestWithBug.balances](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L7) can be used in cross function reentrancies:\n\t- [TestWithBug.withdraw_all()](tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L19-L23)\n",
"first_markdown_element": "tests/detectors/reentrancy-eth/0.8.10/reentrancy_with_non_reentrant.sol#L13-L17",
"id": "bcfa65e776908d618f202fa48f03dde3fbf8397b752d2e8cc3c8e46019e9e174",
"check": "reentrancy-eth", "check": "reentrancy-eth",
"impact": "High", "impact": "High",
"confidence": "Medium" "confidence": "Medium"

Loading…
Cancel
Save