reentrancy-no-eth: do not count staticcalls as reentrant (#1119)

* reentrancy-no-eth: do not count staticcalls as reentrant
* change can_reenter of low-level-calls to false for staticcall
pull/1130/head
alpharush 3 years ago committed by GitHub
parent c57e272d6c
commit bab44b77d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 16
      slither/slithir/operations/high_level_call.py
  2. 2
      slither/slithir/operations/library_call.py
  3. 2
      slither/slithir/operations/low_level_call.py
  4. 20
      tests/detectors/reentrancy-no-eth/0.5.16/no-reentrancy-staticcall.sol
  5. 3
      tests/detectors/reentrancy-no-eth/0.5.16/no-reentrancy-staticcall.sol.0.5.16.ReentrancyReadBeforeWritten.json
  6. 26
      tests/detectors/reentrancy-no-eth/0.6.11/no-reentrancy-staticcall.sol
  7. 3
      tests/detectors/reentrancy-no-eth/0.6.11/no-reentrancy-staticcall.sol.0.6.11.ReentrancyReadBeforeWritten.json
  8. 25
      tests/detectors/reentrancy-no-eth/0.7.6/no-reentrancy-staticcall.sol
  9. 3
      tests/detectors/reentrancy-no-eth/0.7.6/no-reentrancy-staticcall.sol.0.7.6.ReentrancyReadBeforeWritten.json
  10. 15
      tests/test_detectors.py

@ -96,6 +96,14 @@ class HighLevelCall(Call, OperationWithLValue):
# 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):
"""
@ -105,12 +113,8 @@ class HighLevelCall(Call, OperationWithLValue):
: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

@ -17,6 +17,8 @@ class LibraryCall(HighLevelCall):
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:

@ -61,6 +61,8 @@ class LowLevelCall(Call, OperationWithLValue): # pylint: disable=too-many-insta
Must be called after slithIR analysis pass
:return: bool
"""
if self.function_name == "staticcall":
return False
return True
def can_send_eth(self):

@ -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;
}
}

@ -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;
}
}

@ -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;
}
}

@ -116,6 +116,21 @@ ALL_TEST_OBJECTS = [
"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",

Loading…
Cancel
Save