Rewrite 'unchecked_retval' module

pull/79/head
Bernhard Mueller 7 years ago
parent fefd64e062
commit c15b35e2d0
  1. 2
      mythril/analysis/modules/suicide.py
  2. 86
      mythril/analysis/modules/unchecked_retval.py
  3. 4
      solidity_examples/reentrancy.sol
  4. 19
      solidity_examples/returnvalue.sol

@ -79,7 +79,7 @@ def execute(statespace):
break break
# CALLER may also be constrained to hardcoded address. I.e. 'caller' and some integer # CALLER may also be constrained to hardcoded address. I.e. 'caller' and some integer
elif (re.search(r"caller", str(constraint)) and re.search(r'[0-9]{20}', str(constraint))): elif (re.search(r"caller", str(constraint)) and re.search(r'[0-9]{20}', str(constraint))):
can_solve = False can_solve = False

@ -1,9 +1,7 @@
from z3 import *
import re
from mythril.analysis.ops import *
from mythril.analysis.report import Issue from mythril.analysis.report import Issue
from laser.ethereum.svm import NodeFlags
import logging import logging
from laser.ethereum import helper import re
''' '''
@ -30,55 +28,67 @@ def execute(statespace):
logging.debug("Executing module: UNCHECKED_RETVAL") logging.debug("Executing module: UNCHECKED_RETVAL")
issues = [] issues = []
visited = []
for call in statespace.calls: for k in statespace.nodes:
node = statespace.nodes[k]
if NodeFlags.CALL_RETURN in node.flags:
retval_checked = False
for state in node.states:
state = call.state instr = state.get_current_instruction()
address = state.get_current_instruction()['address']
# Only needs to be checked once per call instructions (it's essentially just static analysis) if (instr['opcode'] == 'ISZERO' and re.search(r'retval', str(state.mstate.stack[-1]))):
retval_checked = True
break
if not retval_checked:
address = state.get_current_instruction()['address']
issue = Issue(node.contract_name, node.function_name, address, "Unchecked CALL return value")
issue.description = \
"The return value of an external call is not checked. Note that the contract will continue if the call fails."
issues.append(issue)
if call.state.mstate.pc in visited:
continue
else: else:
visited.append(call.state.mstate.pc)
retval_checked = False nStates = len(node.states)
for idx in range(0, nStates):
# ISZERO retval is expected to be found within the next few instructions. state = node.states[idx]
instr = state.get_current_instruction()
for i in range(0, 10): if (instr['opcode'] == 'CALL'):
_state = call.node.states[call.state_index + i] retval_checked = False
try: for _idx in range(idx, idx + 10):
instr = _state.get_current_instruction()
except IndexError:
break
if (instr['opcode'] == 'ISZERO' and re.search(r'retval', str(_state.mstate.stack[-1]))): try:
retval_checked = True _state = node.states[_idx]
break _instr = _state.get_current_instruction()
if not retval_checked: if (_instr['opcode'] == 'ISZERO' and re.search(r'retval', str(_state .mstate.stack[-1]))):
retval_checked = True
break
issue = Issue(call.node.contract_name, call.node.function_name, address, "Unchecked CALL return value") except IndexError:
break
if (call.to.type == VarType.CONCRETE): if not retval_checked:
receiver = hex(call.to.val)
elif (re.search(r"caller", str(call.to))):
receiver = "msg.sender"
elif (re.search(r"storage", str(call.to))):
receiver = "an address obtained from storage"
else:
receiver = str(call.to)
address = instr['address']
issue = Issue(node.contract_name, node.function_name, address, "Unchecked CALL return value")
issue.description = \ issue.description = \
"The function " + call.node.function_name + " contains a call to " + receiver + ".\n" \ "The return value of an external call is not checked. Note that execution continue even if the called contract throws."
"The return value of this call is not checked. Note that the function will continue to execute with a return value of '0' if the called contract throws."
issues.append(issue) issues.append(issue)
return issues return issues

@ -12,9 +12,7 @@ contract Reentrancy {
function withdraw(uint _amount) public { function withdraw(uint _amount) public {
if(balances[msg.sender] >= _amount) { if(balances[msg.sender] >= _amount) {
if(msg.sender.call.value(_amount)()) { msg.sender.call.value(_amount)();
_amount;
}
balances[msg.sender] -= _amount; balances[msg.sender] -= _amount;
} }
} }

@ -0,0 +1,19 @@
contract ReturnValue {
address callee = 0xE0F7e56e62b4267062172495D7506087205A4229;
function call1() returns (uint256) {
callee.call();
}
function call2() returns (uint256) {
}
function call3() returns (uint256) {
}
}
Loading…
Cancel
Save