Merge branch 'develop' into feature/docs

pull/845/head
Dominik Muhs 6 years ago
parent 0bdc7ed48e
commit d7f80023d7
  1. 2
      README.md
  2. 16
      mythril/analysis/modules/base.py
  3. 4
      mythril/analysis/modules/delegatecall.py
  4. 2
      mythril/analysis/modules/dependence_on_predictable_vars.py
  5. 4
      mythril/analysis/modules/deprecated_ops.py
  6. 2
      mythril/analysis/modules/ether_thief.py
  7. 2
      mythril/analysis/modules/exceptions.py
  8. 9
      mythril/analysis/modules/external_calls.py
  9. 448
      mythril/analysis/modules/integer.py
  10. 171
      mythril/analysis/modules/multiple_sends.py
  11. 7
      mythril/analysis/modules/suicide.py
  12. 1
      mythril/analysis/modules/transaction_order_dependence.py
  13. 160
      mythril/analysis/modules/unchecked_retval.py
  14. 22
      mythril/analysis/security.py
  15. 4
      mythril/analysis/solver.py
  16. 7
      mythril/analysis/symbolic.py
  17. 55
      mythril/laser/ethereum/call.py
  18. 125
      mythril/laser/ethereum/instructions.py
  19. 11
      mythril/laser/ethereum/state/account.py
  20. 4
      mythril/laser/ethereum/state/machine_state.py
  21. 26
      mythril/laser/smt/__init__.py
  22. 1
      tests/graph_test.py
  23. 6
      tests/native_test.py
  24. 103
      tests/testdata/outputs_expected/calls.sol.o.json
  25. 16
      tests/testdata/outputs_expected/calls.sol.o.markdown
  26. 16
      tests/testdata/outputs_expected/calls.sol.o.text
  27. 36
      tests/testdata/outputs_expected/kinds_of_calls.sol.o.json
  28. 30
      tests/testdata/outputs_expected/kinds_of_calls.sol.o.markdown
  29. 28
      tests/testdata/outputs_expected/kinds_of_calls.sol.o.text
  30. 68
      tests/testdata/outputs_expected/overflow.sol.o.json
  31. 20
      tests/testdata/outputs_expected/overflow.sol.o.markdown
  32. 19
      tests/testdata/outputs_expected/overflow.sol.o.text
  33. 43
      tests/testdata/outputs_expected/returnvalue.sol.o.json
  34. 4
      tests/testdata/outputs_expected/returnvalue.sol.o.markdown
  35. 4
      tests/testdata/outputs_expected/returnvalue.sol.o.text
  36. 68
      tests/testdata/outputs_expected/underflow.sol.o.json
  37. 20
      tests/testdata/outputs_expected/underflow.sol.o.markdown
  38. 19
      tests/testdata/outputs_expected/underflow.sol.o.text

@ -15,7 +15,7 @@ Mythril Classic is an open-source security analysis tool for Ethereum smart cont
Whether you want to contribute, need support, or want to learn what we have cooking for the future, our [Discord server](https://discord.gg/E3YrVtG) will serve your needs.
Oh and by the way, we're also building an easy-to-use security analysis platform (a.k.a. "the INFURA for smart contract security") that anybody can use to create purpose-built security tools. It's called [Mythril Platform](https://mythril.ai) and you should definitely [check it out](https://media.consensys.net/mythril-platform-api-is-upping-the-smart-contract-security-game-eee1d2642488).
Oh and by the way, we're also building an easy-to-use security analysis platform (a.k.a. "the INFURA for smart contract security") that anybody can use to create purpose-built security tools. It's called [MythX](https://mythril.ai) and you should definitely [check it out](https://media.consensys.net/mythril-platform-api-is-upping-the-smart-contract-security-game-eee1d2642488).
## Installation and setup

@ -17,21 +17,15 @@ class DetectionModule:
self,
name: str,
swc_id: str,
hooks: List[str],
description: str,
entrypoint: str = "post",
) -> None:
"""Initialize a new detection module.
:param name:
:param swc_id:
:param hooks:
:param description:
:param entrypoint:
"""
pre_hooks: List[str] = None,
post_hooks: List[str] = None,
):
self.name = name
self.swc_id = swc_id
self.hooks = hooks
self.pre_hooks = pre_hooks if pre_hooks else []
self.post_hooks = post_hooks if post_hooks else []
self.description = description
if entrypoint not in ("post", "callback"):
log.error(

@ -22,9 +22,9 @@ class DelegateCallModule(DetectionModule):
super().__init__(
name="DELEGATECALL Usage in Fallback Function",
swc_id=DELEGATECALL_TO_UNTRUSTED_CONTRACT,
hooks=["DELEGATECALL"],
description="Check for invocations of delegatecall(msg.data) in the fallback function.",
entrypoint="callback",
pre_hooks=["DELEGATECALL"],
)
self._issues = []
@ -53,6 +53,8 @@ def _analyze_states(state: GlobalState) -> List[Issue]:
:return: returns the issues for that corresponding state
"""
call = get_call_from_state(state)
if call is None:
return []
issues = []
if call.type is not "DELEGATECALL":

@ -24,13 +24,13 @@ class PredictableDependenceModule(DetectionModule):
super().__init__(
name="Dependence of Predictable Variables",
swc_id="{} {}".format(TIMESTAMP_DEPENDENCE, PREDICTABLE_VARS_DEPENDENCE),
hooks=["CALL", "CALLCODE", "DELEGATECALL", "STATICCALL"],
description=(
"Check for CALLs that send >0 Ether as a result of computation "
"based on predictable variables such as block.coinbase, "
"block.gaslimit, block.timestamp, block.number"
),
entrypoint="callback",
pre_hooks=["CALL", "CALLCODE", "DELEGATECALL", "STATICCALL"],
)
self._issues = []

@ -4,7 +4,9 @@ import logging
from mythril.analysis.modules.base import DetectionModule
from mythril.analysis.report import Issue
from mythril.analysis.swc_data import DEPRICATED_FUNCTIONS_USAGE
from mythril.analysis.modules.base import DetectionModule
from mythril.laser.ethereum.state.global_state import GlobalState
import logging
log = logging.getLogger(__name__)
@ -65,9 +67,9 @@ class DeprecatedOperationsModule(DetectionModule):
super().__init__(
name="Deprecated Operations",
swc_id=DEPRICATED_FUNCTIONS_USAGE,
hooks=["ORIGIN", "CALLCODE"],
description=DESCRIPTION,
entrypoint="callback",
pre_hooks=["ORIGIN", "CALLCODE"],
)
self._issues = []

@ -92,9 +92,9 @@ class EtherThief(DetectionModule):
super().__init__(
name="Ether Thief",
swc_id=UNPROTECTED_ETHER_WITHDRAWAL,
hooks=["CALL"],
description=DESCRIPTION,
entrypoint="callback",
pre_hooks=["CALL"],
)
self._issues = []

@ -63,9 +63,9 @@ class ReachableExceptionsModule(DetectionModule):
super().__init__(
name="Reachable Exceptions",
swc_id=ASSERT_VIOLATION,
hooks=["ASSERT_FAIL"],
description="Checks whether any exception states are reachable.",
entrypoint="callback",
pre_hooks=["ASSERT_FAIL"],
)
self._issues = []

@ -3,12 +3,13 @@ calls."""
import logging
from mythril.analysis import solver
from mythril.analysis.swc_data import REENTRANCY
from mythril.analysis.modules.base import DetectionModule
from mythril.analysis.report import Issue
from mythril.analysis.swc_data import REENTRANCY
from mythril.exceptions import UnsatError
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.laser.smt import UGT, symbol_factory
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.exceptions import UnsatError
import logging
log = logging.getLogger(__name__)
@ -106,9 +107,9 @@ class ExternalCalls(DetectionModule):
super().__init__(
name="External calls",
swc_id=REENTRANCY,
hooks=["CALL"],
description=DESCRIPTION,
entrypoint="callback",
pre_hooks=["CALL"],
)
self._issues = []

@ -1,26 +1,36 @@
"""This module contains the detection code for integer overflows and
underflows."""
import copy
import logging
from mythril.analysis import solver
from mythril.analysis.modules.base import DetectionModule
from mythril.analysis.report import Issue
from mythril.analysis.swc_data import INTEGER_OVERFLOW_AND_UNDERFLOW
from mythril.exceptions import UnsatError
from mythril.laser.ethereum.taint_analysis import TaintRunner
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.analysis.modules.base import DetectionModule
from mythril.laser.smt import (
BitVec,
BVAddNoOverflow,
BVMulNoOverflow,
BVSubNoUnderflow,
Not,
BVMulNoOverflow,
BitVec,
symbol_factory,
Not,
Expression,
)
import logging
log = logging.getLogger(__name__)
class OverUnderflowAnnotation:
def __init__(self, overflowing_state: GlobalState, operator: str, constraint):
self.overflowing_state = overflowing_state
self.operator = operator
self.constraint = constraint
class IntegerOverflowUnderflowModule(DetectionModule):
"""This module searches for integer over- and underflows."""
@ -29,331 +39,179 @@ class IntegerOverflowUnderflowModule(DetectionModule):
super().__init__(
name="Integer Overflow and Underflow",
swc_id=INTEGER_OVERFLOW_AND_UNDERFLOW,
hooks=["ADD", "MUL"],
description=(
"For every SUB instruction, check if there's a possible state "
"where op1 > op0. For every ADD, MUL instruction, check if "
"there's a possible state where op1 + op0 > 2^32 - 1"
),
entrypoint="callback",
pre_hooks=["ADD", "MUL", "SUB", "SSTORE", "JUMPI"],
)
self._issues = []
@property
def issues(self):
return self._issues
def execute(self, statespace):
def execute(self, state: GlobalState):
"""Executes analysis module for integer underflow and integer overflow.
:param statespace: Statespace to analyse
:param state: Statespace to analyse
:return: Found issues
"""
log.debug("Executing module: INTEGER")
issues = []
if state.get_current_instruction()["opcode"] == "ADD":
self._handle_add(state)
elif state.get_current_instruction()["opcode"] == "MUL":
self._handle_mul(state)
elif state.get_current_instruction()["opcode"] == "SUB":
self._handle_sub(state)
elif state.get_current_instruction()["opcode"] == "SSTORE":
self._handle_sstore(state)
elif state.get_current_instruction()["opcode"] == "JUMPI":
self._handle_jumpi(state)
def _handle_add(self, state):
stack = state.mstate.stack
op0, op1 = (
self._make_bitvec_if_not(stack, -1),
self._make_bitvec_if_not(stack, -2),
)
c = Not(BVAddNoOverflow(op0, op1, False))
for k in statespace.nodes:
node = statespace.nodes[k]
# Check satisfiable
model = self._try_constraints(state.node.constraints, [c])
if model is None:
return
for state in node.states:
issues += self._check_integer_underflow(statespace, state, node)
issues += self._check_integer_overflow(statespace, state, node)
annotation = OverUnderflowAnnotation(state, "add", c)
op0.annotate(annotation)
return issues
def _handle_mul(self, state):
stack = state.mstate.stack
op0, op1 = (
self._make_bitvec_if_not(stack, -1),
self._make_bitvec_if_not(stack, -2),
)
def _check_integer_overflow(self, statespace, state, node):
"""Checks for integer overflow.
c = Not(BVMulNoOverflow(op0, op1, False))
:param statespace: statespace that is being examined
:param state: state from node to examine
:param node: node to examine
:return: found issue
"""
issues = []
# Check satisfiable
model = self._try_constraints(state.node.constraints, [c])
if model is None:
return
# Check the instruction
instruction = state.get_current_instruction()
if instruction["opcode"] not in ("ADD", "MUL"):
return issues
annotation = OverUnderflowAnnotation(state, "multiply", c)
op0.annotate(annotation)
# Formulate overflow constraints
def _handle_sub(self, state):
stack = state.mstate.stack
op0, op1 = stack[-1], stack[-2]
# An integer overflow is possible if op0 + op1 or op0 * op1 > MAX_UINT
# Do a type check
allowed_types = [int, BitVec]
if not (type(op0) in allowed_types and type(op1) in allowed_types):
return issues
# Change ints to BitVec
if type(op0) is int:
op0 = symbol_factory.BitVecVal(op0, 256)
if type(op1) is int:
op1 = symbol_factory.BitVecVal(op1, 256)
# Formulate expression
# FIXME: handle exponentiation
if instruction["opcode"] == "ADD":
operator = "add"
expr = op0 + op1
constraint = Not(BVAddNoOverflow(op0, op1, signed=False))
else:
operator = "multiply"
expr = op1 * op0
constraint = Not(BVMulNoOverflow(op0, op1, signed=False))
op0, op1 = (
self._make_bitvec_if_not(stack, -1),
self._make_bitvec_if_not(stack, -2),
)
c = Not(BVSubNoUnderflow(op0, op1, False))
# Check satisfiable
model = self._try_constraints(node.constraints, [constraint])
model = self._try_constraints(state.node.constraints, [c])
if model is None:
log.debug("[INTEGER_OVERFLOW] no model found")
return issues
# Build issue
issue = Issue(
contract=node.contract_name,
function_name=node.function_name,
address=instruction["address"],
swc_id=INTEGER_OVERFLOW_AND_UNDERFLOW,
bytecode=state.environment.code.bytecode,
title="Integer Overflow",
_type="Warning",
gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used),
)
issue.description = "This binary {} operation can result in integer overflow.\n".format(
operator
)
try:
issue.debug = str(
solver.get_transaction_sequence(state, node.constraints + [constraint])
)
except UnsatError:
return issues
issues.append(issue)
return issues
def _verify_integer_overflow(
self, statespace, node, expr, state, model, constraint, op0, op1
):
"""Verifies existence of integer overflow.
:param statespace:
:param node:
:param expr:
:param state:
:param model:
:param constraint:
:param op0:
:param op1:
:return:
"""
# If we get to this point then there has been an integer overflow
# Find out if the overflowed value is actually used
interesting_usages = self._search_children(
statespace,
node,
expr,
constraint=[constraint],
index=node.states.index(state),
)
# Stop if it isn't
if len(interesting_usages) == 0:
return False
return
return self._try_constraints(node.constraints, [Not(constraint)]) is not None
annotation = OverUnderflowAnnotation(state, "subtraction", c)
op0.annotate(annotation)
@staticmethod
def _try_constraints(constraints, new_constraints):
"""Tries new constraints.
:return Model if satisfiable otherwise None
"""
try:
model = solver.get_model(constraints + new_constraints)
return model
except UnsatError:
return None
def _check_integer_underflow(self, statespace, state, node):
"""Checks for integer underflow.
:param statespace:
:param state: state from node to examine
:param node: node to examine
:return: found issue
"""
issues = []
instruction = state.get_current_instruction()
if instruction["opcode"] == "SUB":
stack = state.mstate.stack
op0 = stack[-1]
op1 = stack[-2]
def _make_bitvec_if_not(stack, index):
value = stack[index]
if isinstance(value, BitVec):
return value
stack[index] = symbol_factory.BitVecVal(value, 256)
return stack[index]
def _handle_sstore(self, state):
stack = state.mstate.stack
value = stack[-2]
constraints = copy.deepcopy(node.constraints)
if not isinstance(value, Expression):
return
if type(op0) == int and type(op1) == int:
return []
for annotation in value.annotations:
if not isinstance(annotation, OverUnderflowAnnotation):
continue
log.debug(
"[INTEGER_UNDERFLOW] Checking SUB {0}, {1} at address {2}".format(
str(op0), str(op1), str(instruction["address"])
)
title = (
"Integer Underflow"
if annotation.operator == "subtraction"
else "Integer Overflow"
)
ostate = annotation.overflowing_state
node = ostate.node
issue = Issue(
contract=node.contract_name,
function_name=node.function_name,
address=ostate.get_current_instruction()["address"],
swc_id=INTEGER_OVERFLOW_AND_UNDERFLOW,
bytecode=ostate.environment.code.bytecode,
title=title,
_type="Warning",
gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used),
)
allowed_types = [int, BitVec]
if type(op0) in allowed_types and type(op1) in allowed_types:
constraints.append(Not(BVSubNoUnderflow(op0, op1, signed=False)))
try:
model = solver.get_model(constraints)
# If we get to this point then there has been an integer overflow
# Find out if the overflowed value is actually used
interesting_usages = self._search_children(
statespace, node, (op0 - op1), index=node.states.index(state)
)
# Stop if it isn't
if len(interesting_usages) == 0:
return issues
issue = Issue(
contract=node.contract_name,
function_name=node.function_name,
address=instruction["address"],
swc_id=INTEGER_OVERFLOW_AND_UNDERFLOW,
bytecode=state.environment.code.bytecode,
title="Integer Underflow",
_type="Warning",
gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used),
issue.description = "This binary {} operation can result in {}.\n".format(
annotation.operator, title.lower()
)
try:
issue.debug = str(
solver.get_transaction_sequence(
state, node.constraints + [annotation.constraint]
)
)
except UnsatError:
return
self._issues.append(issue)
issue.description = (
"The subtraction can result in an integer underflow.\n"
)
def _handle_jumpi(self, state):
stack = state.mstate.stack
value = stack[-2]
for annotation in value.annotations:
if not isinstance(annotation, OverUnderflowAnnotation):
continue
ostate = annotation.overflowing_state
node = ostate.node
issue = Issue(
contract=node.contract_name,
function_name=node.function_name,
address=ostate.get_current_instruction()["address"],
swc_id=INTEGER_OVERFLOW_AND_UNDERFLOW,
bytecode=ostate.environment.code.bytecode,
title="Integer Overflow",
_type="Warning",
gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used),
)
issue.debug = str(
solver.get_transaction_sequence(state, node.constraints)
issue.description = "This binary {} operation can result in integer overflow.\n".format(
annotation.operator
)
try:
issue.debug = str(
solver.get_transaction_sequence(
state, node.constraints + [annotation.constraint]
)
issues.append(issue)
except UnsatError:
log.debug("[INTEGER_UNDERFLOW] no model found")
return issues
def _check_usage(self, state, taint_result):
"""Delegates checks to _check_{instruction_name}()
:param state:
:param taint_result:
:return:
"""
opcode = state.get_current_instruction()["opcode"]
if opcode == "JUMPI":
if self._check_jumpi(state, taint_result):
return [state]
elif opcode == "SSTORE":
if self._check_sstore(state, taint_result):
return [state]
return []
@staticmethod
def _check_jumpi(state, taint_result):
"""Check if conditional jump is dependent on the result of expression.
:param state:
:param taint_result:
:return:
"""
assert state.get_current_instruction()["opcode"] == "JUMPI"
return taint_result.check(state, -2)
)
except UnsatError:
return
self._issues.append(issue)
@staticmethod
def _check_sstore(state, taint_result):
"""Check if store operation is dependent on the result of expression.
def _try_constraints(constraints, new_constraints):
"""Tries new constraints.
:param state:
:param taint_result:
:return:
"""
assert state.get_current_instruction()["opcode"] == "SSTORE"
return taint_result.check(state, -2)
def _search_children(
self,
statespace,
node,
expression,
taint_result=None,
constraint=None,
index=0,
depth=0,
max_depth=64,
):
"""Checks the statespace for children states, with JUMPI or SSTORE
instuctions, for dependency on expression.
:param statespace: The statespace to explore
:param node: Current node to explore from
:param expression: Expression to look for
:param taint_result: Result of taint analysis
:param constraint:
:param index: Current state index node.states[index] == current_state
:param depth: Current depth level
:param max_depth: Max depth to explore
:return: List of states that match the opcodes and are dependent on expression
:return Model if satisfiable otherwise None
"""
if constraint is None:
constraint = []
log.debug("SEARCHING NODE for usage of an overflowed variable %d", node.uid)
if taint_result is None:
state = node.states[index]
taint_stack = [False for _ in state.mstate.stack]
taint_stack[-1] = True
taint_result = TaintRunner.execute(
statespace, node, state, initial_stack=taint_stack
)
results = []
if depth >= max_depth:
return []
# Explore current node from index
for j in range(index, len(node.states)):
current_state = node.states[j]
current_instruction = current_state.get_current_instruction()
if current_instruction["opcode"] in ("JUMPI", "SSTORE"):
element = self._check_usage(current_state, taint_result)
if len(element) < 1:
continue
results += element
# Recursively search children
children = [
statespace.nodes[edge.node_to]
for edge in statespace.edges
if edge.node_from == node.uid
# and _try_constraints(statespace.nodes[edge.node_to].constraints, constraint) is not None
]
for child in children:
results += self._search_children(
statespace,
child,
expression,
taint_result,
depth=depth + 1,
max_depth=max_depth,
)
return results
try:
return solver.get_model(constraints + new_constraints)
except UnsatError:
return None
detector = IntegerOverflowUnderflowModule()

@ -1,9 +1,26 @@
"""This module contains the detection code to find multiple sends occurring in
a single transaction."""
from copy import copy
from mythril.analysis.report import Issue
from mythril.analysis.swc_data import MULTIPLE_SENDS
from mythril.analysis.modules.base import DetectionModule
from mythril.laser.ethereum.cfg import JumpType
from mythril.laser.ethereum.state.annotation import StateAnnotation
from mythril.laser.ethereum.state.global_state import GlobalState
import logging
from mythril.analysis.call_helpers import get_call_from_state
log = logging.getLogger(__name__)
class MultipleSendsAnnotation(StateAnnotation):
def __init__(self):
self.calls = []
def __copy__(self):
result = MultipleSendsAnnotation()
result.calls = copy(self.calls)
return result
class MultipleSendsModule(DetectionModule):
@ -14,103 +31,77 @@ class MultipleSendsModule(DetectionModule):
super().__init__(
name="Multiple Sends",
swc_id=MULTIPLE_SENDS,
hooks=["CALL", "DELEGATECALL", "STATICCALL", "CALLCODE"],
description="Check for multiple sends in a single transaction",
entrypoint="callback",
pre_hooks=[
"CALL",
"DELEGATECALL",
"STATICCALL",
"CALLCODE",
"RETURN",
"STOP",
],
)
self._issues = []
def execute(self, statespace):
"""
:param statespace:
:return:
"""
issues = []
for call in statespace.calls:
findings = []
# explore state
findings += self._explore_states(call, statespace)
# explore nodes
findings += self._explore_nodes(call, statespace)
if len(findings) > 0:
node = call.node
instruction = call.state.get_current_instruction()
issue = Issue(
contract=node.contract_name,
function_name=node.function_name,
address=instruction["address"],
swc_id=MULTIPLE_SENDS,
bytecode=call.state.environment.code.bytecode,
title="Multiple Calls",
_type="Informational",
gas_used=(
call.state.mstate.min_gas_used,
call.state.mstate.max_gas_used,
),
)
issue.description = (
"Multiple sends are executed in a single transaction. "
"Try to isolate each external call into its own transaction,"
" as external calls can fail accidentally or deliberately.\nConsecutive calls: \n"
)
for finding in findings:
issue.description += "Call at address: {}\n".format(
finding.state.get_current_instruction()["address"]
)
issues.append(issue)
return issues
def _explore_nodes(self, call, statespace):
def execute(self, state: GlobalState):
"""
:param call:
:param statespace:
:param state:
:return:
"""
children = self._child_nodes(statespace, call.node)
sending_children = list(filter(lambda c: c.node in children, statespace.calls))
return sending_children
@staticmethod
def _explore_states(call, statespace):
"""
:param call:
:param statespace:
:return:
"""
other_calls = list(
filter(
lambda other: other.node == call.node
and other.state_index > call.state_index,
statespace.calls,
self._issues.extend(_analyze_state(state))
return self.issues
@property
def issues(self):
return self._issues
def _analyze_state(state: GlobalState):
"""
:param state: the current state
:return: returns the issues for that corresponding state
"""
node = state.node
instruction = state.get_current_instruction()
annotations = [a for a in state.get_annotations(MultipleSendsAnnotation)]
if len(annotations) == 0:
log.debug("Creating annotation for state")
state.annotate(MultipleSendsAnnotation())
annotations = [a for a in state.get_annotations(MultipleSendsAnnotation)]
calls = annotations[0].calls
if instruction["opcode"] in ["CALL", "DELEGATECALL", "STATICCALL", "CALLCODE"]:
call = get_call_from_state(state)
if call:
calls += [call]
else: # RETURN or STOP
if len(calls) > 1:
issue = Issue(
contract=node.contract_name,
function_name=node.function_name,
address=instruction["address"],
swc_id=MULTIPLE_SENDS,
bytecode=state.environment.code.bytecode,
title="Multiple Calls",
_type="Informational",
gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used),
)
)
return other_calls
def _child_nodes(self, statespace, node):
"""
:param statespace:
:param node:
:return:
"""
result = []
children = [
statespace.nodes[edge.node_to]
for edge in statespace.edges
if edge.node_from == node.uid and edge.type != JumpType.Transaction
]
for child in children:
result.append(child)
result += self._child_nodes(statespace, child)
return result
issue.description = (
"Multiple sends are executed in a single transaction. "
"Try to isolate each external call into its own transaction,"
" as external calls can fail accidentally or deliberately.\nConsecutive calls: \n"
)
for call in calls:
issue.description += "Call at address: {}\n".format(
call.state.get_current_instruction()["address"]
)
return [issue]
return []
detector = MultipleSendsModule()

@ -1,11 +1,10 @@
import logging
from mythril.analysis import solver
from mythril.analysis.modules.base import DetectionModule
from mythril.analysis.report import Issue
from mythril.analysis.swc_data import UNPROTECTED_SELFDESTRUCT
from mythril.exceptions import UnsatError
from mythril.analysis.modules.base import DetectionModule
from mythril.laser.ethereum.state.global_state import GlobalState
import logging
log = logging.getLogger(__name__)
@ -67,9 +66,9 @@ class SuicideModule(DetectionModule):
super().__init__(
name="Unprotected Suicide",
swc_id=UNPROTECTED_SELFDESTRUCT,
hooks=["SUICIDE"],
description=DESCRIPTION,
entrypoint="callback",
pre_hooks=["SUICIDE"],
)
self._issues = []

@ -21,7 +21,6 @@ class TxOrderDependenceModule(DetectionModule):
super().__init__(
name="Transaction Order Dependence",
swc_id=TX_ORDER_DEPENDENCE,
hooks=[],
description=(
"This module finds the existance of transaction order dependence "
"vulnerabilities. The following webpage contains an extensive description "

@ -1,16 +1,30 @@
"""This module contains detection code to find occurrences of calls whose
return value remains unchecked."""
from copy import copy
from mythril.analysis import solver
from mythril.analysis.report import Issue
from mythril.analysis.swc_data import UNCHECKED_RET_VAL
from mythril.analysis.modules.base import DetectionModule
from mythril.exceptions import UnsatError
from mythril.laser.ethereum.state.annotation import StateAnnotation
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.laser.ethereum.svm import NodeFlags
import logging
import re
log = logging.getLogger(__name__)
class UncheckedRetvalAnnotation(StateAnnotation):
def __init__(self):
self.retvals = []
def __copy__(self):
result = UncheckedRetvalAnnotation()
result.retvals = copy(self.retvals)
return result
class UncheckedRetvalModule(DetectionModule):
"""A detection module to test whether CALL return value is checked."""
@ -18,7 +32,6 @@ class UncheckedRetvalModule(DetectionModule):
super().__init__(
name="Unchecked Return Value",
swc_id=UNCHECKED_RET_VAL,
hooks=[],
description=(
"Test whether CALL return value is checked. "
"For direct calls, the Solidity compiler auto-generates this check. E.g.:\n"
@ -28,110 +41,71 @@ class UncheckedRetvalModule(DetectionModule):
"For low-level-calls this check is omitted. E.g.:\n"
' c.call.value(0)(bytes4(sha3("ping(uint256)")),1);'
),
entrypoint="callback",
pre_hooks=["STOP", "RETURN"],
post_hooks=["CALL", "DELEGATECALL", "STATICCALL", "CALLCODE"],
)
self._issues = []
def execute(self, statespace):
def execute(self, state: GlobalState) -> list:
"""
:param statespace:
:param state:
:return:
"""
log.debug("Executing module: UNCHECKED_RETVAL")
issues = []
for k in statespace.nodes:
node = statespace.nodes[k]
if NodeFlags.CALL_RETURN in node.flags:
retval_checked = False
for state in node.states:
instr = state.get_current_instruction()
if instr["opcode"] == "ISZERO" and re.search(
r"retval", str(state.mstate.stack[-1])
):
retval_checked = True
break
self._issues.extend(_analyze_state(state))
return self.issues
if not retval_checked:
@property
def issues(self):
return self._issues
address = state.get_current_instruction()["address"]
issue = Issue(
contract=node.contract_name,
function_name=node.function_name,
address=address,
bytecode=state.environment.code.bytecode,
title="Unchecked CALL return value",
swc_id=UNCHECKED_RET_VAL,
gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used),
)
issue.description = (
"The return value of an external call is not checked. "
"Note that execution continue even if the called contract throws."
)
def _analyze_state(state: GlobalState) -> list:
instruction = state.get_current_instruction()
node = state.node
issues.append(issue)
annotations = [a for a in state.get_annotations(UncheckedRetvalAnnotation)]
if len(annotations) == 0:
state.annotate(UncheckedRetvalAnnotation())
annotations = [a for a in state.get_annotations(UncheckedRetvalAnnotation)]
else:
retvals = annotations[0].retvals
n_states = len(node.states)
for idx in range(
0, n_states - 1
): # Ignore CALLs at last position in a node
state = node.states[idx]
instr = state.get_current_instruction()
if instr["opcode"] == "CALL":
retval_checked = False
for _idx in range(idx, idx + 10):
try:
_state = node.states[_idx]
_instr = _state.get_current_instruction()
if _instr["opcode"] == "ISZERO" and re.search(
r"retval", str(_state.mstate.stack[-1])
):
retval_checked = True
break
except IndexError:
break
if not retval_checked:
address = instr["address"]
issue = Issue(
contract=node.contract_name,
function_name=node.function_name,
bytecode=state.environment.code.bytecode,
address=address,
title="Unchecked CALL return value",
swc_id=UNCHECKED_RET_VAL,
gas_used=(
state.mstate.min_gas_used,
state.mstate.max_gas_used,
),
)
issue.description = (
"The return value of an external call is not checked. "
"Note that execution continue even if the called contract throws."
)
issues.append(issue)
if instruction["opcode"] in ("STOP", "RETURN"):
issues = []
for retval in retvals:
try:
model = solver.get_model(node.constraints + [retval["retval"] == 0])
except UnsatError:
continue
issue = Issue(
contract=node.contract_name,
function_name=node.function_name,
address=retval["address"],
bytecode=state.environment.code.bytecode,
title="Unchecked CALL return value",
swc_id=UNCHECKED_RET_VAL,
gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used),
)
issue.description = (
"The return value of an external call is not checked. "
"Note that execution continue even if the called contract throws."
)
issues.append(issue)
return issues
else:
log.debug("End of call, extracting retval")
assert state.environment.code.instruction_list[state.mstate.pc - 1][
"opcode"
] in ["CALL", "DELEGATECALL", "STATICCALL", "CALLCODE"]
retval = state.mstate.stack[-1]
retvals.append({"address": state.instruction["address"] - 1, "retval": retval})
return []
detector = UncheckedRetvalModule()

@ -1,13 +1,11 @@
"""This module contains functionality for hooking in detection modules and
executing them."""
import importlib.util
import logging
import pkgutil
from collections import defaultdict
from ethereum.opcodes import opcodes
from mythril.analysis import modules
import pkgutil
import importlib.util
import logging
log = logging.getLogger(__name__)
@ -21,16 +19,16 @@ def reset_callback_modules():
module.detector._issues = []
def get_detection_module_hooks(modules):
"""
:param modules:
:return:
"""
def get_detection_module_hooks(modules, hook_type="pre"):
hook_dict = defaultdict(list)
_modules = get_detection_modules(entrypoint="callback", include_modules=modules)
for module in _modules:
for op_code in map(lambda x: x.upper(), module.detector.hooks):
hooks = (
module.detector.pre_hooks
if hook_type == "pre"
else module.detector.post_hooks
)
for op_code in map(lambda x: x.upper(), hooks):
if op_code in OPCODE_LIST:
hook_dict[op_code].append(module.detector.execute)
elif op_code.endswith("*"):

@ -1,5 +1,7 @@
"""This module contains analysis module helpers to solve path constraints."""
from z3 import sat, unknown, FuncInterp
import z3
from mythril.laser.smt import simplify, UGE, Optimize, symbol_factory
from mythril.exceptions import UnsatError
from mythril.laser.ethereum.transaction.transaction_models import (
@ -59,7 +61,7 @@ def pretty_print_model(model):
try:
condition = "0x%x" % model[d].as_long()
except:
condition = str(simplify(model[d]))
condition = str(z3.simplify(model[d]))
ret += "%s: %s\n" % (d.name(), condition)

@ -76,7 +76,12 @@ class SymExecWrapper:
transaction_count=transaction_count,
)
self.laser.register_hooks(
hook_type="pre", hook_dict=get_detection_module_hooks(modules)
hook_type="pre",
hook_dict=get_detection_module_hooks(modules, hook_type="pre"),
)
self.laser.register_hooks(
hook_type="post",
hook_dict=get_detection_module_hooks(modules, hook_type="post"),
)
if isinstance(contract, SolidityContract):

@ -3,19 +3,22 @@ instructions.py to get the necessary elements from the stack and determine the
parameters for the new global state."""
import logging
import re
from typing import Union
from typing import Union, List
from mythril.laser.ethereum import natives
from mythril.laser.ethereum.gas import OPCODE_GAS
from mythril.laser.smt import simplify, Expression, symbol_factory
import mythril.laser.ethereum.util as util
from mythril.laser.ethereum.state.account import Account
from mythril.laser.ethereum.state.calldata import (
CalldataType,
ConcreteCalldata,
SymbolicCalldata,
ConcreteCalldata,
BaseCalldata,
)
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.laser.smt import Expression, simplify, symbol_factory
from mythril.support.loader import DynLoader
import re
"""
This module contains the business logic used by Instruction in instructions.py
@ -194,3 +197,47 @@ def get_call_data(
call_data = SymbolicCalldata("{}_internalcall".format(transaction_id))
return call_data, call_data_type
def native_call(
global_state: GlobalState,
callee_address: str,
call_data: BaseCalldata,
memory_out_offset: Union[int, Expression],
memory_out_size: Union[int, Expression],
) -> Union[List[GlobalState], None]:
if not 0 < int(callee_address, 16) < 5:
return None
log.debug("Native contract called: " + callee_address)
try:
mem_out_start = util.get_concrete_int(memory_out_offset)
mem_out_sz = util.get_concrete_int(memory_out_size)
except TypeError:
log.debug("CALL with symbolic start or offset not supported")
return [global_state]
contract_list = ["ecrecover", "sha256", "ripemd160", "identity"]
call_address_int = int(callee_address, 16)
native_gas_min, native_gas_max = OPCODE_GAS["NATIVE_COST"](
global_state.mstate.calculate_extension_size(mem_out_start, mem_out_sz),
contract_list[call_address_int - 1],
)
global_state.mstate.min_gas_used += native_gas_min
global_state.mstate.max_gas_used += native_gas_max
global_state.mstate.mem_extend(mem_out_start, mem_out_sz)
try:
data = natives.native_contracts(call_address_int, call_data)
except natives.NativeContractException:
for i in range(mem_out_sz):
global_state.mstate.memory[mem_out_start + i] = global_state.new_bitvec(
contract_list[call_address_int - 1] + "(" + str(call_data) + ")", 8
)
return [global_state]
for i in range(
min(len(data), mem_out_sz)
): # If more data is used then it's chopped off
global_state.mstate.memory[mem_out_start + i] = data[i]
# TODO: maybe use BitVec here constrained to 1
return [global_state]

@ -2,31 +2,12 @@
transitions between them."""
import binascii
import logging
from copy import copy, deepcopy
from typing import Callable, List, Union
from ethereum import utils
import mythril.laser.ethereum.natives as natives
import mythril.laser.ethereum.util as helper
from mythril.laser.ethereum import util
from mythril.laser.ethereum.call import get_call_parameters
from mythril.laser.ethereum.evm_exceptions import (
VmException,
StackUnderflowException,
InvalidJumpDestination,
InvalidInstruction,
OutOfGasException,
)
from mythril.laser.ethereum.gas import OPCODE_GAS
from mythril.laser.ethereum.keccak import KeccakFunctionManager
from mythril.laser.ethereum.state.calldata import CalldataType
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.laser.ethereum.transaction import (
MessageCallTransaction,
TransactionStartSignal,
ContractCreationTransaction,
)
from mythril.laser.smt import (
Extract,
Expression,
@ -46,6 +27,26 @@ from mythril.laser.smt import (
Not,
)
from mythril.laser.smt import symbol_factory
import mythril.laser.ethereum.util as helper
from mythril.laser.ethereum import util
from mythril.laser.ethereum.call import get_call_parameters, native_call
from mythril.laser.ethereum.evm_exceptions import (
VmException,
StackUnderflowException,
InvalidJumpDestination,
InvalidInstruction,
OutOfGasException,
)
from mythril.laser.ethereum.gas import OPCODE_GAS
from mythril.laser.ethereum.keccak import KeccakFunctionManager
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.laser.ethereum.transaction import (
MessageCallTransaction,
TransactionStartSignal,
ContractCreationTransaction,
)
from mythril.support.loader import DynLoader
log = logging.getLogger(__name__)
@ -634,8 +635,12 @@ class Instruction:
state = global_state.mstate
val = state.stack.pop()
exp = (val == False) if isinstance(val, Bool) else val == 0
state.stack.append(exp)
exp = Not(val) if isinstance(val, Bool) else val == 0
exp = If(
exp, symbol_factory.BitVecVal(1, 256), symbol_factory.BitVecVal(0, 256)
)
state.stack.append(simplify(exp))
return [global_state]
@ -1327,7 +1332,7 @@ class Instruction:
keccak_keys = filter(keccak_function_manager.is_keccak, storage_keys)
results = []
new = False
new = symbol_factory.Bool(False)
for keccak_key in keccak_keys:
key_argument = keccak_function_manager.get_argument(keccak_key)
@ -1458,9 +1463,9 @@ class Instruction:
negated = (
simplify(Not(condition)) if isinstance(condition, Bool) else condition == 0
)
negated.simplify()
if (type(negated) == bool and negated) or (
isinstance(condition, Bool) and not is_false(negated)
isinstance(negated, Bool) and not is_false(negated)
):
new_state = copy(global_state)
# add JUMPI gas cost
@ -1486,6 +1491,7 @@ class Instruction:
instr = disassembly.instruction_list[index]
condi = simplify(condition) if isinstance(condition, Bool) else condition != 0
condi.simplify()
if instr["opcode"] == "JUMPDEST":
if (type(condi) == bool and condi) or (
isinstance(condi, Bool) and not is_false(condi)
@ -1690,49 +1696,11 @@ class Instruction:
global_state.new_bitvec("retval_" + str(instr["address"]), 256)
)
if 0 < int(callee_address, 16) < 5:
log.debug("Native contract called: " + callee_address)
if call_data == [] and call_data_type == CalldataType.SYMBOLIC:
log.debug("CALL with symbolic data not supported")
return [global_state]
try:
mem_out_start = helper.get_concrete_int(memory_out_offset)
mem_out_sz = helper.get_concrete_int(memory_out_size)
except TypeError:
log.debug("CALL with symbolic start or offset not supported")
return [global_state]
contract_list = ["ecrecover", "sha256", "ripemd160", "identity"]
call_address_int = int(callee_address, 16)
native_gas_min, native_gas_max = OPCODE_GAS["NATIVE_COST"](
global_state.mstate.calculate_extension_size(mem_out_start, mem_out_sz),
contract_list[call_address_int - 1],
)
global_state.mstate.min_gas_used += native_gas_min
global_state.mstate.max_gas_used += native_gas_max
global_state.mstate.mem_extend(mem_out_start, mem_out_sz)
try:
data = natives.native_contracts(call_address_int, call_data)
except natives.NativeContractException:
for i in range(mem_out_sz):
global_state.mstate.memory[
mem_out_start + i
] = global_state.new_bitvec(
contract_list[call_address_int - 1]
+ "("
+ str(call_data)
+ ")",
8,
)
return [global_state]
for i in range(
min(len(data), mem_out_sz)
): # If more data is used then it's chopped off
global_state.mstate.memory[mem_out_start + i] = data[i]
# TODO: maybe use BitVec here constrained to 1
return [global_state]
native_result = native_call(
global_state, callee_address, call_data, memory_out_offset, memory_out_size
)
if native_result:
return native_result
transaction = MessageCallTransaction(
world_state=global_state.world_state,
@ -2033,7 +2001,28 @@ class Instruction:
"""
# TODO: implement me
instr = global_state.get_current_instruction()
try:
callee_address, callee_account, call_data, value, call_data_type, gas, memory_out_offset, memory_out_size = get_call_parameters(
global_state, self.dynamic_loader
)
except ValueError as e:
log.debug(
"Could not determine required parameters for call, putting fresh symbol on the stack. \n{}".format(
e
)
)
global_state.mstate.stack.append(
global_state.new_bitvec("retval_" + str(instr["address"]), 256)
)
return [global_state]
global_state.mstate.stack.append(
global_state.new_bitvec("retval_" + str(instr["address"]), 256)
)
native_result = native_call(
global_state, callee_address, call_data, memory_out_offset, memory_out_size
)
if native_result:
return native_result
return [global_state]

@ -34,11 +34,14 @@ class Storage:
and (self.dynld and self.dynld.storage_loading)
):
try:
self._storage[item] = int(
self.dynld.read_storage(
contract_address=self.address, index=int(item)
self._storage[item] = symbol_factory.BitVecVal(
int(
self.dynld.read_storage(
contract_address=self.address, index=int(item)
),
16,
),
16,
256,
)
return self._storage[item]
except ValueError:

@ -82,8 +82,8 @@ class MachineStack(list):
class MachineState:
"""MachineState represents current machine state also referenced to as
\mu."""
"""MachineState represents current machine state also referenced to as \mu.
"""
def __init__(
self,

@ -26,6 +26,16 @@ class SymbolFactory:
"""A symbol factory provides a default interface for all the components of
mythril to create symbols."""
@staticmethod
def Bool(value: bool, annotations=None):
"""Creates a Bool with concrete value.
:param value: The boolean value
:param annotations: The annotations to initialize the bool with
:return: The freshly created Bool()
"""
raise NotImplementedError
@staticmethod
def BitVecVal(value: int, size: int, annotations=None):
"""Creates a new bit vector with a concrete value.
@ -53,6 +63,17 @@ class _SmtSymbolFactory(SymbolFactory):
"""An implementation of a SymbolFactory that creates symbols using the
classes in: mythril.laser.smt."""
@staticmethod
def Bool(value: bool, annotations=None):
"""Creates a Bool with concrete value.
:param value: The boolean value
:param annotations: The annotations to initialize the bool with
:return: The freshly created Bool()
"""
raw = z3.Bool(value)
return Bool(raw, annotations)
@staticmethod
def BitVecVal(value: int, size: int, annotations=None):
"""Creates a new bit vector with a concrete value."""
@ -70,6 +91,11 @@ class _Z3SymbolFactory(SymbolFactory):
"""An implementation of a SymbolFactory that directly returns z3
symbols."""
@staticmethod
def Bool(value: bool, annotations=None):
"""Creates a new bit vector with a concrete value."""
return z3.Bool(value)
@staticmethod
def BitVecVal(value: int, size: int, annotations=None):
"""Creates a new bit vector with a concrete value."""

@ -28,6 +28,7 @@ class GraphTest(BaseTestCase):
address=(util.get_indexed_address(0)),
strategy="dfs",
transaction_count=1,
execution_timeout=5,
)
html = generate_graph(sym)

@ -14,12 +14,12 @@ IDENTITY_TEST = [(0, False) for _ in range(2)]
# These are Random numbers to check whether the 'if condition' is entered or not
# (True means entered)
SHA256_TEST[0] = (hex(5555555555555555), True)
SHA256_TEST[1] = (hex(323232325445454546), True)
SHA256_TEST[1] = (hex(323232325445454546), False)
SHA256_TEST[2] = (hex(34756834765834658), True)
SHA256_TEST[3] = (hex(8756476956956795876987), True)
SHA256_TEST[3] = (hex(8756476956956795876987), False)
RIPEMD160_TEST[0] = (hex(999999999999999999993), True)
RIPEMD160_TEST[1] = (hex(1111111111112), True)
RIPEMD160_TEST[1] = (hex(1111111111112), False)
ECRECOVER_TEST[0] = (hex(674837568743979857398564869), True)
ECRECOVER_TEST[1] = (hex(3487683476979311), False)

@ -1 +1,102 @@
{"error": null, "issues": [{"address": 661, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable.", "function": "thisisfine()", "max_gas_used": 1254, "min_gas_used": 643, "swc-id": "107", "title": "External call", "type": "Informational"}, {"address": 666, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "thisisfine()", "max_gas_used": 35963, "min_gas_used": 1352, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}, {"address": 779, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable.", "function": "callstoredaddress()", "max_gas_used": 1298, "min_gas_used": 687, "swc-id": "107", "title": "External call", "type": "Informational"}, {"address": 784, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "callstoredaddress()", "max_gas_used": 36007, "min_gas_used": 1396, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}, {"address": 858, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable.", "function": "reentrancy()", "max_gas_used": 1320, "min_gas_used": 709, "swc-id": "107", "title": "External call", "type": "Informational"}, {"address": 871, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "reentrancy()", "max_gas_used": 61043, "min_gas_used": 6432, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}, {"address": 912, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The contract executes a function call with high gas to a user-supplied address. Note that the callee can contain arbitrary code and may re-enter any function in this contract. Review the business logic carefully to prevent unanticipated effects on the contract state.", "function": "calluseraddress(address)", "max_gas_used": 616, "min_gas_used": 335, "swc-id": "107", "title": "External call to user-supplied address", "type": "Warning"}, {"address": 918, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "calluseraddress(address)", "max_gas_used": 35327, "min_gas_used": 1046, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}], "success": true}
{
"error": null,
"issues": [
{
"address": 661,
"contract": "Unknown",
"debug": "<DEBUG-DATA>",
"description": "The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable.",
"function": "thisisfine()",
"max_gas_used": 1254,
"min_gas_used": 643,
"swc-id": "107",
"title": "External call",
"type": "Informational"
},
{
"address": 661,
"contract": "Unknown",
"debug": "<DEBUG-DATA>",
"description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.",
"function": "thisisfine()",
"max_gas_used": 35972,
"min_gas_used": 1361,
"swc-id": "104",
"title": "Unchecked CALL return value",
"type": "Informational"
},
{
"address": 779,
"contract": "Unknown",
"debug": "<DEBUG-DATA>",
"description": "The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable.",
"function": "callstoredaddress()",
"max_gas_used": 1298,
"min_gas_used": 687,
"swc-id": "107",
"title": "External call",
"type": "Informational"
},
{
"address": 779,
"contract": "Unknown",
"debug": "<DEBUG-DATA>",
"description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.",
"function": "callstoredaddress()",
"max_gas_used": 36016,
"min_gas_used": 1405,
"swc-id": "104",
"title": "Unchecked CALL return value",
"type": "Informational"
},
{
"address": 858,
"contract": "Unknown",
"debug": "<DEBUG-DATA>",
"description": "The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable.",
"function": "reentrancy()",
"max_gas_used": 1320,
"min_gas_used": 709,
"swc-id": "107",
"title": "External call",
"type": "Informational"
},
{
"address": 858,
"contract": "Unknown",
"debug": "<DEBUG-DATA>",
"description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.",
"function": "reentrancy()",
"max_gas_used": 61052,
"min_gas_used": 6441,
"swc-id": "104",
"title": "Unchecked CALL return value",
"type": "Informational"
},
{
"address": 912,
"contract": "Unknown",
"debug": "<DEBUG-DATA>",
"description": "The contract executes a function call with high gas to a user-supplied address. Note that the callee can contain arbitrary code and may re-enter any function in this contract. Review the business logic carefully to prevent unanticipated effects on the contract state.",
"function": "calluseraddress(address)",
"max_gas_used": 616,
"min_gas_used": 335,
"swc-id": "107",
"title": "External call to user-supplied address",
"type": "Warning"
},
{
"address": 912,
"contract": "Unknown",
"debug": "<DEBUG-DATA>",
"description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.",
"function": "calluseraddress(address)",
"max_gas_used": 35336,
"min_gas_used": 1055,
"swc-id": "104",
"title": "Unchecked CALL return value",
"type": "Informational"
}
],
"success": true
}

@ -17,8 +17,8 @@ The contract executes a function call to an external address. Verify that the co
- Type: Informational
- Contract: Unknown
- Function name: `thisisfine()`
- PC address: 666
- Estimated Gas Usage: 1352 - 35963
- PC address: 661
- Estimated Gas Usage: 1361 - 35972
### Description
@ -41,8 +41,8 @@ The contract executes a function call to an external address. Verify that the co
- Type: Informational
- Contract: Unknown
- Function name: `callstoredaddress()`
- PC address: 784
- Estimated Gas Usage: 1396 - 36007
- PC address: 779
- Estimated Gas Usage: 1405 - 36016
### Description
@ -65,8 +65,8 @@ The contract executes a function call to an external address. Verify that the co
- Type: Informational
- Contract: Unknown
- Function name: `reentrancy()`
- PC address: 871
- Estimated Gas Usage: 6432 - 61043
- PC address: 858
- Estimated Gas Usage: 6441 - 61052
### Description
@ -89,8 +89,8 @@ The contract executes a function call with high gas to a user-supplied address.
- Type: Informational
- Contract: Unknown
- Function name: `calluseraddress(address)`
- PC address: 918
- Estimated Gas Usage: 1046 - 35327
- PC address: 912
- Estimated Gas Usage: 1055 - 35336
### Description

@ -13,8 +13,8 @@ SWC ID: 104
Type: Informational
Contract: Unknown
Function name: thisisfine()
PC address: 666
Estimated Gas Usage: 1352 - 35963
PC address: 661
Estimated Gas Usage: 1361 - 35972
The return value of an external call is not checked. Note that execution continue even if the called contract throws.
--------------------
@ -33,8 +33,8 @@ SWC ID: 104
Type: Informational
Contract: Unknown
Function name: callstoredaddress()
PC address: 784
Estimated Gas Usage: 1396 - 36007
PC address: 779
Estimated Gas Usage: 1405 - 36016
The return value of an external call is not checked. Note that execution continue even if the called contract throws.
--------------------
@ -53,8 +53,8 @@ SWC ID: 104
Type: Informational
Contract: Unknown
Function name: reentrancy()
PC address: 871
Estimated Gas Usage: 6432 - 61043
PC address: 858
Estimated Gas Usage: 6441 - 61052
The return value of an external call is not checked. Note that execution continue even if the called contract throws.
--------------------
@ -73,8 +73,8 @@ SWC ID: 104
Type: Informational
Contract: Unknown
Function name: calluseraddress(address)
PC address: 918
Estimated Gas Usage: 1046 - 35327
PC address: 912
Estimated Gas Usage: 1055 - 35336
The return value of an external call is not checked. Note that execution continue even if the called contract throws.
--------------------

@ -1,6 +1,18 @@
{
"error": null,
"issues": [
{
"address": 618,
"contract": "Unknown",
"debug": "<DEBUG-DATA>",
"description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.",
"function": "_function_0x141f32ff",
"max_gas_used": 35865,
"min_gas_used": 1113,
"swc-id": "104",
"title": "Unchecked CALL return value",
"type": "Informational"
},
{
"address": 618,
"contract": "Unknown",
@ -14,25 +26,13 @@
"type": "Warning"
},
{
"address": 626,
"contract": "Unknown",
"debug": "<DEBUG-DATA>",
"description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.",
"function": "_function_0x141f32ff",
"max_gas_used": 35856,
"min_gas_used": 1104,
"swc-id": "104",
"title": "Unchecked CALL return value",
"type": "Informational"
},
{
"address": 857,
"address": 849,
"contract": "Unknown",
"debug": "<DEBUG-DATA>",
"description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.",
"function": "_function_0x9b58bc26",
"max_gas_used": 35913,
"min_gas_used": 1161,
"max_gas_used": 35922,
"min_gas_used": 1170,
"swc-id": "104",
"title": "Unchecked CALL return value",
"type": "Informational"
@ -50,13 +50,13 @@
"type": "Warning"
},
{
"address": 1046,
"address": 1038,
"contract": "Unknown",
"debug": "<DEBUG-DATA>",
"description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.",
"function": "_function_0xeea4c864",
"max_gas_used": 35938,
"min_gas_used": 1186,
"max_gas_used": 35947,
"min_gas_used": 1195,
"swc-id": "104",
"title": "Unchecked CALL return value",
"type": "Informational"

@ -1,36 +1,36 @@
# Analysis results for test-filename.sol
## Use of callcode
- SWC ID: 111
- Type: Warning
## Unchecked CALL return value
- SWC ID: 104
- Type: Informational
- Contract: Unknown
- Function name: `_function_0x141f32ff`
- PC address: 618
- Estimated Gas Usage: 389 - 1141
- Estimated Gas Usage: 1113 - 35865
### Description
The function `_function_0x141f32ff` uses callcode. Callcode does not persist sender or value over the call. Use delegatecall instead.
The return value of an external call is not checked. Note that execution continue even if the called contract throws.
## Unchecked CALL return value
- SWC ID: 104
- Type: Informational
## Use of callcode
- SWC ID: 111
- Type: Warning
- Contract: Unknown
- Function name: `_function_0x141f32ff`
- PC address: 626
- Estimated Gas Usage: 1104 - 35856
- PC address: 618
- Estimated Gas Usage: 389 - 1141
### Description
The return value of an external call is not checked. Note that execution continue even if the called contract throws.
The function `_function_0x141f32ff` uses callcode. Callcode does not persist sender or value over the call. Use delegatecall instead.
## Unchecked CALL return value
- SWC ID: 104
- Type: Informational
- Contract: Unknown
- Function name: `_function_0x9b58bc26`
- PC address: 857
- Estimated Gas Usage: 1161 - 35913
- PC address: 849
- Estimated Gas Usage: 1170 - 35922
### Description
@ -53,8 +53,8 @@ The contract executes a function call with high gas to a user-supplied address.
- Type: Informational
- Contract: Unknown
- Function name: `_function_0xeea4c864`
- PC address: 1046
- Estimated Gas Usage: 1186 - 35938
- PC address: 1038
- Estimated Gas Usage: 1195 - 35947
### Description

@ -1,3 +1,13 @@
==== Unchecked CALL return value ====
SWC ID: 104
Type: Informational
Contract: Unknown
Function name: _function_0x141f32ff
PC address: 618
Estimated Gas Usage: 1113 - 35865
The return value of an external call is not checked. Note that execution continue even if the called contract throws.
--------------------
==== Use of callcode ====
SWC ID: 111
Type: Warning
@ -8,23 +18,13 @@ Estimated Gas Usage: 389 - 1141
The function `_function_0x141f32ff` uses callcode. Callcode does not persist sender or value over the call. Use delegatecall instead.
--------------------
==== Unchecked CALL return value ====
SWC ID: 104
Type: Informational
Contract: Unknown
Function name: _function_0x141f32ff
PC address: 626
Estimated Gas Usage: 1104 - 35856
The return value of an external call is not checked. Note that execution continue even if the called contract throws.
--------------------
==== Unchecked CALL return value ====
SWC ID: 104
Type: Informational
Contract: Unknown
Function name: _function_0x9b58bc26
PC address: 857
Estimated Gas Usage: 1161 - 35913
PC address: 849
Estimated Gas Usage: 1170 - 35922
The return value of an external call is not checked. Note that execution continue even if the called contract throws.
--------------------
@ -43,8 +43,8 @@ SWC ID: 104
Type: Informational
Contract: Unknown
Function name: _function_0xeea4c864
PC address: 1046
Estimated Gas Usage: 1186 - 35938
PC address: 1038
Estimated Gas Usage: 1195 - 35947
The return value of an external call is not checked. Note that execution continue even if the called contract throws.
--------------------

@ -1,30 +1,42 @@
{
"error": null,
"issues": [
{
"address": 567,
"contract": "Unknown",
"debug": "<DEBUG-DATA>",
"description": "The subtraction can result in an integer underflow.\n",
"function": "sendeth(address,uint256)",
"max_gas_used": 1035,
"min_gas_used": 750,
"swc-id": "101",
"title": "Integer Underflow",
"type": "Warning"
},
{
"address": 649,
"contract": "Unknown",
"debug": "<DEBUG-DATA>",
"description": "The subtraction can result in an integer underflow.\n",
"function": "sendeth(address,uint256)",
"max_gas_used": 1758,
"min_gas_used": 1283,
"swc-id": "101",
"title": "Integer Underflow",
"type": "Warning"
}
],
"success": true
"error":null,
"issues":[
{
"address":567,
"contract":"Unknown",
"debug":"<DEBUG-DATA>",
"description":"This binary subtraction operation can result in integer overflow.\n",
"function":"sendeth(address,uint256)",
"max_gas_used":1053,
"min_gas_used":768,
"swc-id":"101",
"title":"Integer Overflow",
"type":"Warning"
},
{
"address":567,
"contract":"Unknown",
"debug":"<DEBUG-DATA>",
"description":"This binary subtraction operation can result in integer underflow.\n",
"function":"sendeth(address,uint256)",
"max_gas_used":1774,
"min_gas_used":1299,
"swc-id":"101",
"title":"Integer Underflow",
"type":"Warning"
},
{
"address":649,
"contract":"Unknown",
"debug":"<DEBUG-DATA>",
"description":"This binary subtraction operation can result in integer underflow.\n",
"function":"sendeth(address,uint256)",
"max_gas_used":1774,
"min_gas_used":1299,
"swc-id":"101",
"title":"Integer Underflow",
"type":"Warning"
}
],
"success":true
}

@ -1,16 +1,28 @@
# Analysis results for test-filename.sol
## Integer Overflow
- SWC ID: 101
- Type: Warning
- Contract: Unknown
- Function name: `sendeth(address,uint256)`
- PC address: 567
- Estimated Gas Usage: 768 - 1053
### Description
This binary subtraction operation can result in integer overflow.
## Integer Underflow
- SWC ID: 101
- Type: Warning
- Contract: Unknown
- Function name: `sendeth(address,uint256)`
- PC address: 567
- Estimated Gas Usage: 750 - 1035
- Estimated Gas Usage: 1299 - 1774
### Description
The subtraction can result in an integer underflow.
This binary subtraction operation can result in integer underflow.
## Integer Underflow
- SWC ID: 101
@ -18,8 +30,8 @@ The subtraction can result in an integer underflow.
- Contract: Unknown
- Function name: `sendeth(address,uint256)`
- PC address: 649
- Estimated Gas Usage: 1283 - 1758
- Estimated Gas Usage: 1299 - 1774
### Description
The subtraction can result in an integer underflow.
This binary subtraction operation can result in integer underflow.

@ -1,11 +1,22 @@
==== Integer Overflow ====
SWC ID: 101
Type: Warning
Contract: Unknown
Function name: sendeth(address,uint256)
PC address: 567
Estimated Gas Usage: 768 - 1053
This binary subtraction operation can result in integer overflow.
--------------------
==== Integer Underflow ====
SWC ID: 101
Type: Warning
Contract: Unknown
Function name: sendeth(address,uint256)
PC address: 567
Estimated Gas Usage: 750 - 1035
The subtraction can result in an integer underflow.
Estimated Gas Usage: 1299 - 1774
This binary subtraction operation can result in integer underflow.
--------------------
@ -15,8 +26,8 @@ Type: Warning
Contract: Unknown
Function name: sendeth(address,uint256)
PC address: 649
Estimated Gas Usage: 1283 - 1758
The subtraction can result in an integer underflow.
Estimated Gas Usage: 1299 - 1774
This binary subtraction operation can result in integer underflow.
--------------------

@ -1 +1,42 @@
{"error": null, "issues": [{"address": 196, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable.", "function": "callchecked()", "max_gas_used": 1210, "min_gas_used": 599, "swc-id": "107", "title": "External call", "type": "Informational"}, {"address": 285, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable.", "function": "callnotchecked()", "max_gas_used": 1232, "min_gas_used": 621, "swc-id": "107", "title": "External call", "type": "Informational"}, {"address": 290, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "callnotchecked()", "max_gas_used": 35941, "min_gas_used": 1330, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}], "success": true}
{
"error": null,
"issues": [
{
"address": 196,
"contract": "Unknown",
"debug": "<DEBUG-DATA>",
"description": "The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable.",
"function": "callchecked()",
"max_gas_used": 1210,
"min_gas_used": 599,
"swc-id": "107",
"title": "External call",
"type": "Informational"
},
{
"address": 285,
"contract": "Unknown",
"debug": "<DEBUG-DATA>",
"description": "The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable.",
"function": "callnotchecked()",
"max_gas_used": 1232,
"min_gas_used": 621,
"swc-id": "107",
"title": "External call",
"type": "Informational"
},
{
"address": 285,
"contract": "Unknown",
"debug": "<DEBUG-DATA>",
"description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.",
"function": "callnotchecked()",
"max_gas_used": 35950,
"min_gas_used": 1339,
"swc-id": "104",
"title": "Unchecked CALL return value",
"type": "Informational"
}
],
"success": true
}

@ -29,8 +29,8 @@ The contract executes a function call to an external address. Verify that the co
- Type: Informational
- Contract: Unknown
- Function name: `callnotchecked()`
- PC address: 290
- Estimated Gas Usage: 1330 - 35941
- PC address: 285
- Estimated Gas Usage: 1339 - 35950
### Description

@ -23,8 +23,8 @@ SWC ID: 104
Type: Informational
Contract: Unknown
Function name: callnotchecked()
PC address: 290
Estimated Gas Usage: 1330 - 35941
PC address: 285
Estimated Gas Usage: 1339 - 35950
The return value of an external call is not checked. Note that execution continue even if the called contract throws.
--------------------

@ -1,30 +1,42 @@
{
"error": null,
"issues": [
{
"address": 567,
"contract": "Unknown",
"debug": "<DEBUG-DATA>",
"description": "The subtraction can result in an integer underflow.\n",
"function": "sendeth(address,uint256)",
"max_gas_used": 1035,
"min_gas_used": 750,
"swc-id": "101",
"title": "Integer Underflow",
"type": "Warning"
},
{
"address": 649,
"contract": "Unknown",
"debug": "<DEBUG-DATA>",
"description": "The subtraction can result in an integer underflow.\n",
"function": "sendeth(address,uint256)",
"max_gas_used": 1758,
"min_gas_used": 1283,
"swc-id": "101",
"title": "Integer Underflow",
"type": "Warning"
}
],
"success": true
"error":null,
"issues":[
{
"address":567,
"contract":"Unknown",
"debug":"<DEBUG-DATA>",
"description":"This binary subtraction operation can result in integer overflow.\n",
"function":"sendeth(address,uint256)",
"max_gas_used":1053,
"min_gas_used":768,
"swc-id":"101",
"title":"Integer Overflow",
"type":"Warning"
},
{
"address":567,
"contract":"Unknown",
"debug":"<DEBUG-DATA>",
"description":"This binary subtraction operation can result in integer underflow.\n",
"function":"sendeth(address,uint256)",
"max_gas_used":1774,
"min_gas_used":1299,
"swc-id":"101",
"title":"Integer Underflow",
"type":"Warning"
},
{
"address":649,
"contract":"Unknown",
"debug":"<DEBUG-DATA>",
"description":"This binary subtraction operation can result in integer underflow.\n",
"function":"sendeth(address,uint256)",
"max_gas_used":1774,
"min_gas_used":1299,
"swc-id":"101",
"title":"Integer Underflow",
"type":"Warning"
}
],
"success":true
}

@ -1,16 +1,28 @@
# Analysis results for test-filename.sol
## Integer Overflow
- SWC ID: 101
- Type: Warning
- Contract: Unknown
- Function name: `sendeth(address,uint256)`
- PC address: 567
- Estimated Gas Usage: 768 - 1053
### Description
This binary subtraction operation can result in integer overflow.
## Integer Underflow
- SWC ID: 101
- Type: Warning
- Contract: Unknown
- Function name: `sendeth(address,uint256)`
- PC address: 567
- Estimated Gas Usage: 750 - 1035
- Estimated Gas Usage: 1299 - 1774
### Description
The subtraction can result in an integer underflow.
This binary subtraction operation can result in integer underflow.
## Integer Underflow
- SWC ID: 101
@ -18,8 +30,8 @@ The subtraction can result in an integer underflow.
- Contract: Unknown
- Function name: `sendeth(address,uint256)`
- PC address: 649
- Estimated Gas Usage: 1283 - 1758
- Estimated Gas Usage: 1299 - 1774
### Description
The subtraction can result in an integer underflow.
This binary subtraction operation can result in integer underflow.

@ -1,11 +1,22 @@
==== Integer Overflow ====
SWC ID: 101
Type: Warning
Contract: Unknown
Function name: sendeth(address,uint256)
PC address: 567
Estimated Gas Usage: 768 - 1053
This binary subtraction operation can result in integer overflow.
--------------------
==== Integer Underflow ====
SWC ID: 101
Type: Warning
Contract: Unknown
Function name: sendeth(address,uint256)
PC address: 567
Estimated Gas Usage: 750 - 1035
The subtraction can result in an integer underflow.
Estimated Gas Usage: 1299 - 1774
This binary subtraction operation can result in integer underflow.
--------------------
@ -15,8 +26,8 @@ Type: Warning
Contract: Unknown
Function name: sendeth(address,uint256)
PC address: 649
Estimated Gas Usage: 1283 - 1758
The subtraction can result in an integer underflow.
Estimated Gas Usage: 1299 - 1774
This binary subtraction operation can result in integer underflow.
--------------------

Loading…
Cancel
Save