Skip to content

Commit

Permalink
reentrancy-no-eth: do not count staticcalls as reentrant (#1119)
Browse files Browse the repository at this point in the history
* reentrancy-no-eth: do not count staticcalls as reentrant
* change can_reenter of low-level-calls to false for staticcall
  • Loading branch information
0xalpharush authored Mar 18, 2022
1 parent c57e272 commit bab44b7
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 6 deletions.
16 changes: 10 additions & 6 deletions slither/slithir/operations/high_level_call.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,14 @@ def type_call(self):
# region Analyses
###################################################################################
###################################################################################
def is_static_call(self):
# If solidity >0.5, STATICCALL is used
if self.compilation_unit.solc_version and self.compilation_unit.solc_version >= "0.5.0":
if isinstance(self.function, Function) and (self.function.view or self.function.pure):
return True
if isinstance(self.function, Variable):
return True
return False

def can_reenter(self, callstack=None):
"""
Expand All @@ -105,12 +113,8 @@ def can_reenter(self, callstack=None):
:param callstack: check for recursion
:return: bool
"""
# If solidity >0.5, STATICCALL is used
if self.compilation_unit.solc_version and self.compilation_unit.solc_version >= "0.5.0":
if isinstance(self.function, Function) and (self.function.view or self.function.pure):
return False
if isinstance(self.function, Variable):
return False
if self.is_static_call():
return False
# If there is a call to itself
# We can check that the function called is
# reentrancy-safe
Expand Down
2 changes: 2 additions & 0 deletions slither/slithir/operations/library_call.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ def can_reenter(self, callstack=None):
Must be called after slithIR analysis pass
:return: bool
"""
if self.is_static_call():
return False
# In case of recursion, return False
callstack = [] if callstack is None else callstack
if self.function in callstack:
Expand Down
2 changes: 2 additions & 0 deletions slither/slithir/operations/low_level_call.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ def can_reenter(self, _callstack=None):
Must be called after slithIR analysis pass
:return: bool
"""
if self.function_name == "staticcall":
return False
return True

def can_send_eth(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
library MyLibrary {

function aViewCall(address token) internal view {
(bool success ,) = token.staticcall(abi.encodeWithSignature("decimals"));
require(success, "call failed");
}
}

contract A {
uint256 private protectMe = 1;
function good() external {
MyLibrary.aViewCall(0x6B175474E89094C44Da98b954EedeAC495271d0F);
protectMe += 1;
}
function good1() external {
(bool success,) = address(MyLibrary).staticcall(abi.encodeWithSignature("aViewCall(address)"));
require(success, "call failed");
protectMe += 1;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[
[]
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
interface IERC20 {
function decimals() external view returns (uint8);
}

library MyLibrary {

function aViewCall(address token) internal view {
(bool success , ) = token.staticcall(
abi.encodeWithSelector(IERC20.decimals.selector)
);
require(success, "call failed");
}
}

contract A {
uint256 private protectMe = 1;
function good() external {
MyLibrary.aViewCall(0x6B175474E89094C44Da98b954EedeAC495271d0F);
protectMe += 1;
}
function good1() external {
(bool success,) = address(MyLibrary).staticcall(abi.encodeWithSignature("aViewCall(address)"));
require(success, "call failed");
protectMe += 1;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[
[]
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
interface IERC20 {
function decimals() external view returns (uint8);
}

library MyLibrary {

function aViewCall(address token) internal view {
(bool success , ) = token.staticcall(
abi.encodeWithSelector(IERC20.decimals.selector)
);
require(success, "call failed");
}
}

contract A {
uint256 private protectMe = 1; function good() external {
MyLibrary.aViewCall(0x6B175474E89094C44Da98b954EedeAC495271d0F);
protectMe += 1;
}
function good1() external {
(bool success,) = address(MyLibrary).staticcall(abi.encodeWithSignature("aViewCall(address)"));
require(success, "call failed");
protectMe += 1;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[
[]
]
15 changes: 15 additions & 0 deletions tests/test_detectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,21 @@ def id_test(test_item: Test):
"DAO.sol",
"0.4.25",
),
Test(
all_detectors.ReentrancyReadBeforeWritten,
"no-reentrancy-staticcall.sol",
"0.5.16",
),
Test(
all_detectors.ReentrancyReadBeforeWritten,
"no-reentrancy-staticcall.sol",
"0.6.11",
),
Test(
all_detectors.ReentrancyReadBeforeWritten,
"no-reentrancy-staticcall.sol",
"0.7.6",
),
Test(
all_detectors.BooleanEquality,
"boolean-constant-equality.sol",
Expand Down

0 comments on commit bab44b7

Please sign in to comment.