Add unchecked_retval test:

pull/30/head
Bernhard Mueller 7 years ago
parent 32da74bd99
commit f89b0f8b5c
  1. 2
      myth
  2. 30
      mythril/analysis/modules/delegatecall_to_dynamic.py
  3. 33
      mythril/analysis/modules/unchecked_retval.py

@ -10,7 +10,7 @@ from mythril.ether.ethcontract import ETHContract
from mythril.ether.util import compile_solidity
from mythril.rpc.client import EthJsonRpc
from mythril.ipc.client import EthIpc
from mythril.rpc.exceptions import *
from mythril.rpc.exceptions import ConnectionError
from mythril.support.loader import DynLoader
from mythril.exceptions import CompilerError
from mythril.analysis.symbolic import StateSpace

@ -0,0 +1,30 @@
from z3 import *
from mythril.analysis.ops import *
from mythril.analysis.report import Issue
'''
MODULE DESCRIPTION:
Check for invocations of delegatecall to a dynamic contract address (e.g. taken from storage).
'''
def execute(statespace):
issues = []
for call in statespace.calls:
if (call.type == "DELEGATECALL"):
if (call.to.type == VarType.SYMBOLIC):
issue = Issue("DELEGATECALL to dynamic address", "Informational")
issue.description = \
"The function " + call.node.function_name + " in contract '" + call.node.module_name + " delegates execution to a contract with a dynamic address." \
"To address:" + str(call.to)
issues.append(issue)
return issues

@ -31,20 +31,39 @@ def execute(statespace):
# The instructions executed in each node (basic block) are saved in node.instruction_list, e.g.:
# [{address: "132", opcode: "CALL"}, {address: "133", opcode: "ISZERO"}]
next_i = helper.get_instruction_index(call.node.instruction_list, call.addr) + 1
instr = call.node.instruction_list[next_i]
start_index = helper.get_instruction_index(call.node.instruction_list, call.addr) + 1
logging.info("CALL. Next instruction: " + instr['opcode'])
retval_checked = False
# ISZERO retval should be found within the next 10 instructions.
# The stack contents at a particular point of execution are found in node.states[address].stack
if (instr['opcode'] != 'ISZERO' or not re.search(r'retval', str(call.node.states[call.addr + 1].stack[-1]))):
for i in range(0, 10):
try:
instr = call.node.instruction_list[start_index + i]
except IndexError:
break
if (instr['opcode'] == 'ISZERO' and re.search(r'retval', str(call.node.states[instr['address']].stack[-1]))):
retval_checked = True
break
if not retval_checked:
issue = Issue("Unchecked CALL return value")
if (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)
issue.description = \
"The function " + call.node.function_name + " in contract '" + call.node.module_name + "' contains a call to " + str(call.to) + ".\n" \
"The return value of this call is not checked. Note that the function will continue to execute even if the called contract throws."
"The function " + call.node.function_name + " in contract '" + call.node.module_name + "' contains a call to " + receiver + ".\n" \
"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)

Loading…
Cancel
Save