Merge with develop

pull/803/head
Nikhil Parasaram 6 years ago
commit 4346565ab8
  1. 1
      .circleci/config.yml
  2. 4
      mythril/analysis/modules/base.py
  3. 1
      mythril/analysis/modules/delegatecall.py
  4. 12
      mythril/analysis/modules/dependence_on_predictable_vars.py
  5. 5
      mythril/analysis/modules/deprecated_ops.py
  6. 4
      mythril/analysis/modules/ether_thief.py
  7. 7
      mythril/analysis/modules/exceptions.py
  8. 8
      mythril/analysis/modules/external_calls.py
  9. 13
      mythril/analysis/modules/integer.py
  10. 8
      mythril/analysis/modules/suicide.py
  11. 5
      mythril/analysis/modules/transaction_order_dependence.py
  12. 4
      mythril/analysis/modules/unchecked_retval.py
  13. 4
      mythril/analysis/report.py
  14. 11
      mythril/analysis/security.py
  15. 13
      mythril/analysis/solver.py
  16. 1
      mythril/disassembler/disassembly.py
  17. 6
      mythril/ethereum/interface/leveldb/accountindexing.py
  18. 4
      mythril/ethereum/interface/leveldb/client.py
  19. 6
      mythril/ethereum/interface/rpc/client.py
  20. 22
      mythril/interfaces/cli.py
  21. 40
      mythril/laser/ethereum/call.py
  22. 134
      mythril/laser/ethereum/instructions.py
  23. 4
      mythril/laser/ethereum/natives.py
  24. 5
      mythril/laser/ethereum/state/machine_state.py
  25. 104
      mythril/laser/ethereum/state/memory.py
  26. 34
      mythril/laser/ethereum/svm.py
  27. 12
      mythril/laser/ethereum/taint_analysis.py
  28. 5
      mythril/laser/ethereum/transaction/symbolic.py
  29. 1
      mythril/laser/ethereum/transaction/transaction_models.py
  30. 1
      mythril/laser/ethereum/util.py
  31. 20
      mythril/mythril.py
  32. 8
      mythril/support/loader.py
  33. 9
      mythril/support/signatures.py
  34. 4
      mythril/support/truffle.py
  35. 5
      requirements.txt
  36. 5
      setup.py
  37. 43
      tests/laser/state/mstate_test.py
  38. 4
      tests/native_tests.sol
  39. 67
      tests/testdata/outputs_expected/kinds_of_calls.sol.o.json
  40. 6
      tests/testdata/outputs_expected/kinds_of_calls.sol.o.markdown
  41. 6
      tests/testdata/outputs_expected/kinds_of_calls.sol.o.text

@ -48,6 +48,7 @@ jobs:
name: Unit-testing
command: tox
working_directory: /home/mythril
no_output_timeout: 10m
environment:
LC_ALL: en_US.ASCII
LANG: en_US.ASCII

@ -1,6 +1,8 @@
import logging
from typing import List
log = logging.getLogger(__name__)
class DetectionModule:
def __init__(
@ -16,7 +18,7 @@ class DetectionModule:
self.hooks = hooks
self.description = description
if entrypoint not in ("post", "callback"):
logging.error(
log.error(
"Invalid entrypoint in module %s, must be one of {post, callback}",
self.name,
)

@ -3,7 +3,6 @@ from mythril.analysis.swc_data import DELEGATECALL_TO_UNTRUSTED_CONTRACT
from mythril.analysis.ops import get_variable, VarType
from mythril.analysis.report import Issue
from mythril.analysis.modules.base import DetectionModule
import logging
class DelegateCallModule(DetectionModule):

@ -9,6 +9,8 @@ from mythril.analysis.modules.base import DetectionModule
from mythril.exceptions import UnsatError
import logging
log = logging.getLogger(__name__)
class PredictableDependenceModule(DetectionModule):
def __init__(self):
@ -30,6 +32,7 @@ class PredictableDependenceModule(DetectionModule):
return self._issues
def execute(self, state: GlobalState) -> list:
log.debug("Executing module: DEPENDENCE_ON_PREDICTABLE_VARS")
self._issues.extend(_analyze_states(state))
return self.issues
@ -43,7 +46,7 @@ def _analyze_states(state: GlobalState) -> list:
if call is None:
return []
if "callvalue" in str(call.value):
logging.debug("[DEPENDENCE_ON_PREDICTABLE_VARS] Skipping refund function")
log.debug("[DEPENDENCE_ON_PREDICTABLE_VARS] Skipping refund function")
return []
# We're only interested in calls that send Ether
@ -182,14 +185,15 @@ def _analyze_states(state: GlobalState) -> list:
def solve(call: Call) -> bool:
try:
model = solver.get_model(call.node.constraints)
logging.debug("[DEPENDENCE_ON_PREDICTABLE_VARS] MODEL: " + str(model))
log.debug("[DEPENDENCE_ON_PREDICTABLE_VARS] MODEL: " + str(model))
pretty_model = solver.pretty_print_model(model)
logging.debug(
log.debug(
"[DEPENDENCE_ON_PREDICTABLE_VARS] main model: \n%s" % pretty_model
)
return True
except UnsatError:
logging.debug("[DEPENDENCE_ON_PREDICTABLE_VARS] no model found")
log.debug("[DEPENDENCE_ON_PREDICTABLE_VARS] no model found")
return False

@ -4,6 +4,7 @@ from mythril.analysis.modules.base import DetectionModule
from mythril.laser.ethereum.state.global_state import GlobalState
import logging
log = logging.getLogger(__name__)
DESCRIPTION = """
Check for usage of deprecated opcodes
@ -15,7 +16,7 @@ def _analyze_state(state):
instruction = state.get_current_instruction()
if instruction["opcode"] == "ORIGIN":
logging.debug("ORIGIN in function " + node.function_name)
log.debug("ORIGIN in function " + node.function_name)
title = "Use of tx.origin"
description = (
"The function `{}` retrieves the transaction origin (tx.origin) using the ORIGIN opcode. "
@ -27,7 +28,7 @@ def _analyze_state(state):
swc_id = DEPRICATED_FUNCTIONS_USAGE
elif instruction["opcode"] == "CALLCODE":
logging.debug("CALLCODE in function " + node.function_name)
log.debug("CALLCODE in function " + node.function_name)
title = "Use of callcode"
description = (
"The function `{}` uses callcode. Callcode does not persist sender or value over the call. "

@ -9,6 +9,8 @@ from z3 import BitVecVal, UGT, Sum
import logging
from copy import copy
log = logging.getLogger(__name__)
DESCRIPTION = """
Search for cases where Ether can be withdrawn to a user-specified address.
@ -68,7 +70,7 @@ def _analyze_state(state):
gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used),
)
except UnsatError:
logging.debug("[ETHER_THIEF] no model found")
log.debug("[ETHER_THIEF] no model found")
return []
return [issue]

@ -6,6 +6,9 @@ from mythril.analysis.modules.base import DetectionModule
import logging
log = logging.getLogger(__name__)
class ReachableExceptionsModule(DetectionModule):
def __init__(self):
super().__init__(
@ -17,7 +20,7 @@ class ReachableExceptionsModule(DetectionModule):
def execute(self, statespace):
logging.debug("Executing module: EXCEPTIONS")
log.debug("Executing module: EXCEPTIONS")
issues = []
@ -65,7 +68,7 @@ class ReachableExceptionsModule(DetectionModule):
)
except UnsatError:
logging.debug("[EXCEPTIONS] no model found")
log.debug("[EXCEPTIONS] no model found")
return issues

@ -7,6 +7,8 @@ from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.exceptions import UnsatError
import logging
log = logging.getLogger(__name__)
DESCRIPTION = """
Search for low level calls (e.g. call.value()) that forward all gas to the callee.
@ -27,7 +29,7 @@ def _analyze_state(state):
try:
constraints = node.constraints
transaction_sequence = solver.get_transaction_sequence(
state, constraints + [UGT(gas, 2300)]
state, constraints + [UGT(gas, BitVecVal(2300, 256))]
)
# Check whether we can also set the callee address
@ -58,7 +60,7 @@ def _analyze_state(state):
except UnsatError:
logging.debug(
log.debug(
"[EXTERNAL_CALLS] Callee address cannot be modified. Reporting informational issue."
)
@ -82,7 +84,7 @@ def _analyze_state(state):
)
except UnsatError:
logging.debug("[EXTERNAL_CALLS] No model found.")
log.debug("[EXTERNAL_CALLS] No model found.")
return []
return [issue]

@ -11,6 +11,9 @@ import copy
import logging
log = logging.getLogger(__name__)
class IntegerOverflowUnderflowModule(DetectionModule):
def __init__(self):
super().__init__(
@ -30,7 +33,7 @@ class IntegerOverflowUnderflowModule(DetectionModule):
:param statespace: Statespace to analyse
:return: Found issues
"""
logging.debug("Executing module: INTEGER")
log.debug("Executing module: INTEGER")
issues = []
@ -89,7 +92,7 @@ class IntegerOverflowUnderflowModule(DetectionModule):
model = self._try_constraints(node.constraints, [constraint])
if model is None:
logging.debug("[INTEGER_OVERFLOW] no model found")
log.debug("[INTEGER_OVERFLOW] no model found")
return issues
# Build issue
@ -170,7 +173,7 @@ class IntegerOverflowUnderflowModule(DetectionModule):
if type(op0) == int and type(op1) == int:
return []
logging.debug(
log.debug(
"[INTEGER_UNDERFLOW] Checking SUB {0}, {1} at address {2}".format(
str(op0), str(op1), str(instruction["address"])
)
@ -215,7 +218,7 @@ class IntegerOverflowUnderflowModule(DetectionModule):
issues.append(issue)
except UnsatError:
logging.debug("[INTEGER_UNDERFLOW] no model found")
log.debug("[INTEGER_UNDERFLOW] no model found")
return issues
def _check_usage(self, state, taint_result):
@ -266,7 +269,7 @@ class IntegerOverflowUnderflowModule(DetectionModule):
if constraint is None:
constraint = []
logging.debug("SEARCHING NODE for usage of an overflowed variable %d", node.uid)
log.debug("SEARCHING NODE for usage of an overflowed variable %d", node.uid)
if taint_result is None:
state = node.states[index]

@ -7,6 +7,8 @@ from mythril.analysis.modules.base import DetectionModule
from mythril.laser.ethereum.state.global_state import GlobalState
import logging
log = logging.getLogger(__name__)
DESCRIPTION = """
Check if the contact can be 'accidentally' killed by anyone.
For kill-able contracts, also check whether it is possible to direct the contract balance to the attacker.
@ -14,12 +16,12 @@ For kill-able contracts, also check whether it is possible to direct the contrac
def _analyze_state(state):
logging.info("Suicide module: Analyzing suicide instruction")
log.info("Suicide module: Analyzing suicide instruction")
node = state.node
instruction = state.get_current_instruction()
to = state.mstate.stack[-1]
logging.debug("[SUICIDE] SUICIDE in function " + node.function_name)
log.debug("[SUICIDE] SUICIDE in function " + node.function_name)
try:
try:
@ -52,7 +54,7 @@ def _analyze_state(state):
)
return [issue]
except UnsatError:
logging.info("[UNCHECKED_SUICIDE] no model found")
log.info("[UNCHECKED_SUICIDE] no model found")
return []

@ -10,6 +10,9 @@ from mythril.analysis.modules.base import DetectionModule
from mythril.exceptions import UnsatError
log = logging.getLogger(__name__)
class TxOrderDependenceModule(DetectionModule):
def __init__(self):
super().__init__(
@ -26,7 +29,7 @@ class TxOrderDependenceModule(DetectionModule):
def execute(self, statespace):
""" Executes the analysis module"""
logging.debug("Executing module: TOD")
log.debug("Executing module: TOD")
issues = []

@ -6,6 +6,8 @@ from mythril.laser.ethereum.svm import NodeFlags
import logging
import re
log = logging.getLogger(__name__)
class UncheckedRetvalModule(DetectionModule):
def __init__(self):
@ -26,7 +28,7 @@ class UncheckedRetvalModule(DetectionModule):
def execute(self, statespace):
logging.debug("Executing module: UNCHECKED_RETVAL")
log.debug("Executing module: UNCHECKED_RETVAL")
issues = []

@ -5,6 +5,8 @@ from jinja2 import PackageLoader, Environment
import _pysha3 as sha3
import hashlib
log = logging.getLogger(__name__)
class Issue:
def __init__(
@ -39,7 +41,7 @@ class Issue:
keccak.update(bytes.fromhex(bytecode))
self.bytecode_hash = "0x" + keccak.hexdigest()
except ValueError:
logging.debug(
log.debug(
"Unable to change the bytecode to bytes. Bytecode: {}".format(bytecode)
)
self.bytecode_hash = ""

@ -5,6 +5,7 @@ import pkgutil
import importlib.util
import logging
log = logging.getLogger(__name__)
OPCODE_LIST = [c[0] for _, c in opcodes.items()]
@ -27,7 +28,7 @@ def get_detection_module_hooks(modules):
for actual_hook in to_register:
hook_dict[actual_hook].append(module.detector.execute)
else:
logging.error(
log.error(
"Encountered invalid hook opcode %s in module %s",
op_code,
module.detector.name,
@ -54,24 +55,24 @@ def get_detection_modules(entrypoint, include_modules=()):
if module.__name__ != "base" and module.detector.entrypoint == entrypoint:
_modules.append(module)
logging.info("Found %s detection modules", len(_modules))
log.info("Found %s detection modules", len(_modules))
return _modules
def fire_lasers(statespace, module_names=()):
logging.info("Starting analysis")
log.info("Starting analysis")
issues = []
for module in get_detection_modules(
entrypoint="post", include_modules=module_names
):
logging.info("Executing " + module.detector.name)
log.info("Executing " + module.detector.name)
issues += module.detector.execute(statespace)
for module in get_detection_modules(
entrypoint="callback", include_modules=module_names
):
logging.debug("Retrieving results for " + module.detector.name)
log.debug("Retrieving results for " + module.detector.name)
issues += module.detector.issues
reset_callback_modules()

@ -5,6 +5,8 @@ from mythril.laser.ethereum.transaction.transaction_models import (
)
import logging
log = logging.getLogger(__name__)
def get_model(constraints, minimize=(), maximize=()):
s = Optimize()
@ -27,7 +29,7 @@ def get_model(constraints, minimize=(), maximize=()):
if result == sat:
return s.model()
elif result == unknown:
logging.debug("Timeout encountered while solving expression using z3")
log.debug("Timeout encountered while solving expression using z3")
raise UnsatError
@ -76,7 +78,6 @@ def get_transaction_sequence(global_state, constraints):
minimize = []
transactions = []
model = None
for transaction in transaction_sequence:
tx_id = str(transaction.id)
if not isinstance(transaction, ContractCreationTransaction):
@ -92,16 +93,10 @@ def get_transaction_sequence(global_state, constraints):
concrete_transactions[tx_id] = tx_template.copy()
try:
model = get_model(tx_constraints, minimize=minimize)
break
except UnsatError:
continue
else:
creation_tx_ids.append(tx_id)
if model is None:
model = get_model(tx_constraints, minimize=minimize)
model = get_model(tx_constraints, minimize=minimize)
for transaction in transactions:
tx_id = str(transaction.id)

@ -1,7 +1,6 @@
from mythril.ethereum import util
from mythril.disassembler import asm
from mythril.support.signatures import SignatureDB
import logging
class Disassembly(object):

@ -8,6 +8,8 @@ from ethereum import utils
from ethereum.utils import hash32, address, int256
from mythril.exceptions import AddressNotFoundError
log = logging.getLogger(__name__)
BATCH_SIZE = 8 * 4096
@ -76,9 +78,7 @@ class AccountIndexer(object):
"""
Processesing method
"""
logging.debug(
"Processing blocks %d to %d" % (startblock, startblock + BATCH_SIZE)
)
log.debug("Processing blocks %d to %d" % (startblock, startblock + BATCH_SIZE))
addresses = []

@ -13,6 +13,8 @@ from mythril.ethereum.interface.leveldb.eth_db import ETH_DB
from mythril.ethereum.evmcontract import EVMContract
from mythril.exceptions import AddressNotFoundError
log = logging.getLogger(__name__)
# Per https://github.com/ethereum/go-ethereum/blob/master/core/rawdb/schema.go
# prefixes and suffixes for keys in geth
header_prefix = b"h" # header_prefix + num (uint64 big endian) + hash -> header
@ -213,7 +215,7 @@ class EthLevelDB(object):
cnt += 1
if not cnt % 1000:
logging.info("Searched %d contracts" % cnt)
log.info("Searched %d contracts" % cnt)
def contract_hash_to_address(self, contract_hash):
"""Tries to find corresponding account address"""

@ -11,6 +11,8 @@ from .exceptions import (
)
from .base_client import BaseClient
log = logging.getLogger(__name__)
GETH_DEFAULT_RPC_PORT = 8545
ETH_DEFAULT_RPC_PORT = 8545
PARITY_DEFAULT_RPC_PORT = 8545
@ -44,7 +46,7 @@ class EthJsonRpc(BaseClient):
scheme += "s"
url = "{}://{}:{}".format(scheme, self.host, self.port)
headers = {"Content-Type": JSON_MEDIA_TYPE}
logging.debug("rpc send: %s" % json.dumps(data))
log.debug("rpc send: %s" % json.dumps(data))
try:
r = self.session.post(url, headers=headers, data=json.dumps(data))
except RequestsConnectionError:
@ -53,7 +55,7 @@ class EthJsonRpc(BaseClient):
raise BadStatusCodeError(r.status_code)
try:
response = r.json()
logging.debug("rpc response: %s" % response)
log.debug("rpc response: %s" % response)
except ValueError:
raise BadJsonError(r.text)
try:

@ -18,10 +18,12 @@ from mythril.mythril import Mythril
from mythril.version import VERSION
import mythril.support.signatures as sigs
log = logging.getLogger(__name__)
def exit_with_error(format_, message):
if format_ == "text" or format_ == "markdown":
logging.error(message)
log.error(message)
else:
result = {"success": False, "error": str(message), "issues": []}
print(json.dumps(result))
@ -191,7 +193,9 @@ def main():
options.add_argument(
"--enable-physics", action="store_true", help="enable graph physics simulation"
)
options.add_argument("-v", type=int, help="log level (0-2)", metavar="LOG_LEVEL")
options.add_argument(
"-v", type=int, help="log level (0-5)", metavar="LOG_LEVEL", default=2
)
options.add_argument(
"-q",
"--query-signature",
@ -246,11 +250,19 @@ def main():
sys.exit()
if args.v:
if 0 <= args.v < 3:
if 0 <= args.v < 6:
log_levels = [
logging.NOTSET,
logging.CRITICAL,
logging.ERROR,
logging.WARNING,
logging.INFO,
logging.DEBUG,
]
coloredlogs.install(
fmt="%(name)s[%(process)d] %(levelname)s %(message)s",
level=[logging.NOTSET, logging.INFO, logging.DEBUG][args.v],
fmt="%(name)s [%(levelname)s]: %(message)s", level=log_levels[args.v]
)
logging.getLogger("mythril").setLevel(log_levels[args.v])
else:
exit_with_error(
args.outform, "Invalid -v value, you can find valid values in usage"

@ -17,6 +17,8 @@ This module contains the business logic used by Instruction in instructions.py
to get the necessary elements from the stack and determine the parameters for the new global state.
"""
log = logging.getLogger(__name__)
def get_call_parameters(
global_state: GlobalState, dynamic_loader: DynLoader, with_value=False
@ -74,16 +76,16 @@ def get_callee_address(
try:
callee_address = hex(util.get_concrete_int(symbolic_to_address))
except TypeError:
logging.debug("Symbolic call encountered")
log.debug("Symbolic call encountered")
match = re.search(r"storage_(\d+)", str(simplify(symbolic_to_address)))
logging.debug("CALL to: " + str(simplify(symbolic_to_address)))
log.debug("CALL to: " + str(simplify(symbolic_to_address)))
if match is None or dynamic_loader is None:
raise ValueError()
index = int(match.group(1))
logging.debug("Dynamic contract address at storage index {}".format(index))
log.debug("Dynamic contract address at storage index {}".format(index))
# attempt to read the contract address from instance storage
try:
@ -92,7 +94,7 @@ def get_callee_address(
)
# TODO: verify whether this happens or not
except:
logging.debug("Error accessing contract storage.")
log.debug("Error accessing contract storage.")
raise ValueError
# testrpc simply returns the address, geth response is more elaborate.
@ -119,22 +121,22 @@ def get_callee_account(
return global_state.accounts[callee_address]
except KeyError:
# We have a valid call address, but contract is not in the modules list
logging.debug("Module with address " + callee_address + " not loaded.")
log.debug("Module with address " + callee_address + " not loaded.")
if dynamic_loader is None:
raise ValueError()
logging.debug("Attempting to load dependency")
log.debug("Attempting to load dependency")
try:
code = dynamic_loader.dynld(environment.active_account.address, callee_address)
except ValueError as error:
logging.debug("Unable to execute dynamic loader because: {}".format(str(error)))
log.debug("Unable to execute dynamic loader because: {}".format(str(error)))
raise error
if code is None:
logging.debug("No code returned, not a contract account?")
log.debug("No code returned, not a contract account?")
raise ValueError()
logging.debug("Dependency loaded: " + callee_address)
log.debug("Dependency loaded: " + callee_address)
callee_account = Account(
callee_address, code, callee_address, dynamic_loader=dynamic_loader
@ -159,30 +161,18 @@ def get_call_data(
state = global_state.mstate
transaction_id = "{}_internalcall".format(global_state.current_transaction.id)
try:
# TODO: This only allows for either fully concrete or fully symbolic calldata.
# Improve management of memory and callata to support a mix between both types.
calldata_from_mem = state.memory[
util.get_concrete_int(memory_start) : util.get_concrete_int(
memory_start + memory_size
)
]
i = 0
starting_calldata = []
while i < len(calldata_from_mem):
elem = calldata_from_mem[i]
if isinstance(elem, int):
starting_calldata.append(elem)
i += 1
else: # BitVec
for j in range(0, elem.size(), 8):
starting_calldata.append(Extract(j + 7, j, elem))
i += 1
call_data = ConcreteCalldata(transaction_id, starting_calldata)
call_data = ConcreteCalldata(transaction_id, calldata_from_mem)
call_data_type = CalldataType.CONCRETE
logging.debug("Calldata: " + str(call_data))
log.debug("Calldata: " + str(call_data))
except TypeError:
logging.debug("Unsupported symbolic calldata offset")
log.debug("Unsupported symbolic calldata offset")
call_data_type = CalldataType.SYMBOLIC
call_data = SymbolicCalldata("{}_internalcall".format(transaction_id))

@ -53,6 +53,8 @@ from mythril.laser.ethereum.transaction import (
from mythril.support.loader import DynLoader
from mythril.analysis.solver import get_model
log = logging.getLogger(__name__)
TT256 = 2 ** 256
TT256M1 = 2 ** 256 - 1
@ -132,7 +134,7 @@ class Instruction:
def evaluate(self, global_state: GlobalState, post=False) -> List[GlobalState]:
""" Performs the mutation for this instruction """
# Generalize some ops
logging.debug("Evaluating {}".format(self.op_code))
log.debug("Evaluating {}".format(self.op_code))
op = self.op_code.lower()
if self.op_code.startswith("PUSH"):
op = "push"
@ -245,7 +247,7 @@ class Instruction:
else:
result = 0
except TypeError:
logging.debug("BYTE: Unsupported symbolic byte offset")
log.debug("BYTE: Unsupported symbolic byte offset")
result = global_state.new_bitvec(
str(simplify(op1)) + "[" + str(simplify(op0)) + "]", 256
)
@ -481,14 +483,14 @@ class Instruction:
try:
mstart = util.get_concrete_int(op0)
except TypeError:
logging.debug("Unsupported symbolic memory offset in CALLDATACOPY")
log.debug("Unsupported symbolic memory offset in CALLDATACOPY")
return [global_state]
dstart_sym = False
try:
dstart = util.get_concrete_int(op1)
except TypeError:
logging.debug("Unsupported symbolic calldata offset in CALLDATACOPY")
log.debug("Unsupported symbolic calldata offset in CALLDATACOPY")
dstart = simplify(op1)
dstart_sym = True
@ -496,7 +498,7 @@ class Instruction:
try:
size = util.get_concrete_int(op2)
except TypeError:
logging.debug("Unsupported symbolic size in CALLDATACOPY")
log.debug("Unsupported symbolic size in CALLDATACOPY")
size = simplify(op2)
size_sym = True
@ -510,7 +512,7 @@ class Instruction:
+ ": + "
+ str(size)
+ "]",
256,
8,
)
return [global_state]
@ -518,7 +520,7 @@ class Instruction:
try:
state.mem_extend(mstart, size)
except TypeError:
logging.debug(
log.debug(
"Memory allocation error: mstart = "
+ str(mstart)
+ ", size = "
@ -533,7 +535,7 @@ class Instruction:
+ ": + "
+ str(size)
+ "]",
256,
8,
)
return [global_state]
@ -551,7 +553,7 @@ class Instruction:
state.memory[i + mstart] = new_memory[i]
except IndexError:
logging.debug("Exception copying calldata to memory")
log.debug("Exception copying calldata to memory")
state.memory[mstart] = global_state.new_bitvec(
"calldata_"
@ -561,7 +563,7 @@ class Instruction:
+ ": + "
+ str(size)
+ "]",
256,
8,
)
return [global_state]
@ -643,7 +645,7 @@ class Instruction:
return [global_state]
keccak = utils.sha3(utils.bytearray_to_bytestr(data))
logging.debug("Computed SHA3 Hash: " + str(binascii.hexlify(keccak)))
log.debug("Computed SHA3 Hash: " + str(binascii.hexlify(keccak)))
state.stack.append(BitVecVal(util.concrete_int_from_bytes(keccak, 0), 256))
return [global_state]
@ -664,7 +666,7 @@ class Instruction:
try:
concrete_memory_offset = helper.get_concrete_int(memory_offset)
except TypeError:
logging.debug("Unsupported symbolic memory offset in CODECOPY")
log.debug("Unsupported symbolic memory offset in CODECOPY")
return [global_state]
try:
@ -680,14 +682,14 @@ class Instruction:
"code({})".format(
global_state.environment.active_account.contract_name
),
256,
8,
)
return [global_state]
try:
concrete_code_offset = helper.get_concrete_int(code_offset)
except TypeError:
logging.debug("Unsupported symbolic code offset in CODECOPY")
log.debug("Unsupported symbolic code offset in CODECOPY")
global_state.mstate.mem_extend(concrete_memory_offset, size)
for i in range(size):
global_state.mstate.memory[
@ -696,7 +698,7 @@ class Instruction:
"code({})".format(
global_state.environment.active_account.contract_name
),
256,
8,
)
return [global_state]
@ -713,7 +715,7 @@ class Instruction:
"code({})".format(
global_state.environment.active_account.contract_name
),
256,
8,
)
return [global_state]
@ -734,7 +736,7 @@ class Instruction:
"code({})".format(
global_state.environment.active_account.contract_name
),
256,
8,
)
return [global_state]
@ -747,14 +749,14 @@ class Instruction:
try:
addr = hex(helper.get_concrete_int(addr))
except TypeError:
logging.debug("unsupported symbolic address for EXTCODESIZE")
log.debug("unsupported symbolic address for EXTCODESIZE")
state.stack.append(global_state.new_bitvec("extcodesize_" + str(addr), 256))
return [global_state]
try:
code = self.dynamic_loader.dynld(environment.active_account.address, addr)
except (ValueError, AttributeError) as e:
logging.debug("error accessing contract storage due to: " + str(e))
log.debug("error accessing contract storage due to: " + str(e))
state.stack.append(global_state.new_bitvec("extcodesize_" + str(addr), 256))
return [global_state]
@ -829,23 +831,20 @@ class Instruction:
state = global_state.mstate
op0 = state.stack.pop()
logging.debug("MLOAD[" + str(op0) + "]")
log.debug("MLOAD[" + str(op0) + "]")
try:
offset = util.get_concrete_int(op0)
except TypeError:
logging.debug("Can't MLOAD from symbolic index")
log.debug("Can't MLOAD from symbolic index")
data = global_state.new_bitvec("mem[" + str(simplify(op0)) + "]", 256)
state.stack.append(data)
return [global_state]
try:
state.mem_extend(offset, 32)
data = util.concrete_int_from_bytes(state.memory, offset)
except TypeError: # Symbolic memory
# TODO: Handle this properly
data = state.memory[offset]
logging.debug("Load from memory[" + str(offset) + "]: " + str(data))
state.mem_extend(offset, 32)
data = state.memory.get_word_at(offset)
log.debug("Load from memory[" + str(offset) + "]: " + str(data))
state.stack.append(data)
return [global_state]
@ -858,28 +857,17 @@ class Instruction:
try:
mstart = util.get_concrete_int(op0)
except TypeError:
logging.debug("MSTORE to symbolic index. Not supported")
log.debug("MSTORE to symbolic index. Not supported")
return [global_state]
try:
state.mem_extend(mstart, 32)
except Exception:
logging.debug(
"Error extending memory, mstart = " + str(mstart) + ", size = 32"
)
log.debug("Error extending memory, mstart = " + str(mstart) + ", size = 32")
logging.debug("MSTORE to mem[" + str(mstart) + "]: " + str(value))
log.debug("MSTORE to mem[" + str(mstart) + "]: " + str(value))
try:
# Attempt to concretize value
_bytes = util.concrete_int_to_bytes(value)
assert len(_bytes) == 32
state.memory[mstart : mstart + 32] = _bytes
except:
try:
state.memory[mstart] = value
except TypeError:
logging.debug("Invalid memory access")
state.memory.write_word_at(mstart, value)
return [global_state]
@ -891,12 +879,19 @@ class Instruction:
try:
offset = util.get_concrete_int(op0)
except TypeError:
logging.debug("MSTORE to symbolic index. Not supported")
log.debug("MSTORE to symbolic index. Not supported")
return [global_state]
state.mem_extend(offset, 1)
state.memory[offset] = value % 256
try:
value_to_write = util.get_concrete_int(value) ^ 0xFF
except TypeError: # BitVec
value_to_write = Extract(7, 0, value)
log.debug("MSTORE8 to mem[" + str(offset) + "]: " + str(value_to_write))
state.memory[offset] = value_to_write
return [global_state]
@StateTransition()
@ -905,7 +900,7 @@ class Instruction:
state = global_state.mstate
index = state.stack.pop()
logging.debug("Storage access at index " + str(index))
log.debug("Storage access at index " + str(index))
try:
index = util.get_concrete_int(index)
@ -973,7 +968,7 @@ class Instruction:
global keccak_function_manager
state = global_state.mstate
index, value = state.stack.pop(), state.stack.pop()
logging.debug("Write to storage[" + str(index) + "]")
log.debug("Write to storage[" + str(index) + "]")
try:
index = util.get_concrete_int(index)
@ -1037,7 +1032,7 @@ class Instruction:
value if not isinstance(value, ExprRef) else simplify(value)
)
except KeyError:
logging.debug("Error writing to storage: Invalid index")
log.debug("Error writing to storage: Invalid index")
if constraint is not None:
global_state.mstate.constraints.append(constraint)
@ -1090,7 +1085,7 @@ class Instruction:
try:
jump_addr = util.get_concrete_int(op0)
except TypeError:
logging.debug("Skipping JUMPI to invalid destination.")
log.debug("Skipping JUMPI to invalid destination.")
global_state.mstate.pc += 1
global_state.mstate.min_gas_used += min_gas
global_state.mstate.max_gas_used += max_gas
@ -1115,14 +1110,14 @@ class Instruction:
new_state.mstate.constraints.append(negated)
states.append(new_state)
else:
logging.debug("Pruned unreachable states.")
log.debug("Pruned unreachable states.")
# True case
# Get jump destination
index = util.get_instruction_index(disassembly.instruction_list, jump_addr)
if not index:
logging.debug("Invalid jump destination: " + str(jump_addr))
log.debug("Invalid jump destination: " + str(jump_addr))
return states
instr = disassembly.instruction_list[index]
@ -1143,7 +1138,7 @@ class Instruction:
new_state.mstate.constraints.append(condi)
states.append(new_state)
else:
logging.debug("Pruned unreachable states.")
log.debug("Pruned unreachable states.")
del global_state
return states
@ -1186,13 +1181,13 @@ class Instruction:
def return_(self, global_state: GlobalState):
state = global_state.mstate
offset, length = state.stack.pop(), state.stack.pop()
return_data = [global_state.new_bitvec("return_data", 256)]
return_data = [global_state.new_bitvec("return_data", 8)]
try:
return_data = state.memory[
util.get_concrete_int(offset) : util.get_concrete_int(offset + length)
]
except TypeError:
logging.debug("Return with symbolic length or offset. Not supported")
log.debug("Return with symbolic length or offset. Not supported")
global_state.current_transaction.end(global_state, return_data)
@StateTransition()
@ -1230,13 +1225,13 @@ class Instruction:
def revert_(self, global_state: GlobalState) -> None:
state = global_state.mstate
offset, length = state.stack.pop(), state.stack.pop()
return_data = [global_state.new_bitvec("return_data", 256)]
return_data = [global_state.new_bitvec("return_data", 8)]
try:
return_data = state.memory[
util.get_concrete_int(offset) : util.get_concrete_int(offset + length)
]
except TypeError:
logging.debug("Return with symbolic length or offset. Not supported")
log.debug("Return with symbolic length or offset. Not supported")
global_state.current_transaction.end(
global_state, return_data=return_data, revert=True
)
@ -1265,7 +1260,7 @@ class Instruction:
global_state, self.dynamic_loader, True
)
except ValueError as e:
logging.debug(
log.debug(
"Could not determine required parameters for call, putting fresh symbol on the stack. \n{}".format(
e
)
@ -1280,16 +1275,20 @@ class Instruction:
)
if 0 < int(callee_address, 16) < 5:
logging.debug("Native contract called: " + callee_address)
log.debug("Native contract called: " + callee_address)
if call_data == [] and call_data_type == CalldataType.SYMBOLIC:
logging.debug("CALL with symbolic data not supported")
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 = memory_out_size.as_long()
mem_out_sz = (
memory_out_size
if type(memory_out_size) == int
else memory_out_size.as_long()
)
except TypeError:
logging.debug("CALL with symbolic start or offset not supported")
log.debug("CALL with symbolic start or offset not supported")
return [global_state]
contract_list = ["ecrecover", "sha256", "ripemd160", "identity"]
@ -1312,7 +1311,7 @@ class Instruction:
+ "("
+ str(call_data)
+ ")",
256,
8,
)
return [global_state]
@ -1320,7 +1319,6 @@ class Instruction:
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]
@ -1346,7 +1344,7 @@ class Instruction:
global_state, self.dynamic_loader, True
)
except ValueError as e:
logging.debug(
log.debug(
"Could not determine required parameters for call, putting fresh symbol on the stack. \n{}".format(
e
)
@ -1409,7 +1407,7 @@ class Instruction:
global_state, self.dynamic_loader, True
)
except ValueError as e:
logging.debug(
log.debug(
"Could not determine required parameters for call, putting fresh symbol on the stack. \n{}".format(
e
)
@ -1442,7 +1440,7 @@ class Instruction:
global_state, self.dynamic_loader, True
)
except ValueError as e:
logging.debug(
log.debug(
"Could not determine required parameters for call, putting fresh symbol on the stack. \n{}".format(
e
)
@ -1503,7 +1501,7 @@ class Instruction:
global_state, self.dynamic_loader
)
except ValueError as e:
logging.debug(
log.debug(
"Could not determine required parameters for call, putting fresh symbol on the stack. \n{}".format(
e
)
@ -1536,7 +1534,7 @@ class Instruction:
global_state, self.dynamic_loader
)
except ValueError as e:
logging.debug(
log.debug(
"Could not determine required parameters for call, putting fresh symbol on the stack. \n{}".format(
e
)

@ -12,6 +12,8 @@ from mythril.laser.ethereum.state.calldata import BaseCalldata, ConcreteCalldata
from mythril.laser.ethereum.util import bytearray_to_int, sha3, get_concrete_int
from z3 import Concat, simplify
log = logging.getLogger(__name__)
class NativeContractException(Exception):
pass
@ -51,7 +53,7 @@ def ecrecover(data: Union[bytes, str, List[int]]) -> bytes:
try:
pub = ecrecover_to_pub(message, v, r, s)
except Exception as e:
logging.debug("An error has occured while extracting public key: " + e)
log.debug("An error has occured while extracting public key: " + e)
return []
o = [0] * 12 + [x for x in sha3(pub)[-20:]]
return o

@ -10,6 +10,7 @@ from mythril.laser.ethereum.evm_exceptions import (
OutOfGasException,
)
from mythril.laser.ethereum.state.constraints import Constraints
from mythril.laser.ethereum.state.memory import Memory
class MachineStack(list):
@ -88,7 +89,7 @@ class MachineState:
""" Constructor for machineState """
self.pc = pc
self.stack = MachineStack(stack)
self.memory = memory or []
self.memory = memory or Memory()
self.gas_limit = gas_limit
self.min_gas_used = min_gas_used # lower gas usage bound
self.max_gas_used = max_gas_used # upper gas usage bound
@ -128,7 +129,7 @@ class MachineState:
self.min_gas_used += extend_gas
self.max_gas_used += extend_gas
self.check_gas()
self.memory.extend(bytearray(m_extend))
self.memory.extend(m_extend)
def memory_write(self, offset: int, data: List[int]) -> None:
""" Writes data to memory starting at offset """

@ -0,0 +1,104 @@
from typing import Union
from z3 import BitVecRef, Extract, BitVecVal, If, BoolRef, Z3Exception, simplify, Concat
from mythril.laser.ethereum import util
class Memory:
def __init__(self):
self._memory = []
def __len__(self):
return len(self._memory)
def extend(self, size):
self._memory.extend(bytearray(size))
def get_word_at(self, index: int) -> Union[int, BitVecRef]:
"""
Access a word from a specified memory index
:param index: integer representing the index to access
:return: 32 byte word at the specified index
"""
try:
return util.concrete_int_from_bytes(
bytes([util.get_concrete_int(b) for b in self[index : index + 32]]), 0
)
except TypeError:
result = simplify(
Concat(
[
b if isinstance(b, BitVecRef) else BitVecVal(b, 8)
for b in self[index : index + 32]
]
)
)
assert result.size() == 256
return result
def write_word_at(
self, index: int, value: Union[int, BitVecRef, bool, BoolRef]
) -> None:
"""
Writes a 32 byte word to memory at the specified index`
:param index: index to write to
:param value: the value to write to memory
"""
try:
# Attempt to concretize value
if isinstance(value, bool):
_bytes = (
int(1).to_bytes(32, byteorder="big")
if value
else int(0).to_bytes(32, byteorder="big")
)
else:
_bytes = util.concrete_int_to_bytes(value)
assert len(_bytes) == 32
self[index : index + 32] = _bytes
except (Z3Exception, AttributeError): # BitVector or BoolRef
if isinstance(value, BoolRef):
value_to_write = If(value, BitVecVal(1, 256), BitVecVal(0, 256))
else:
value_to_write = value
assert value_to_write.size() == 256
for i in range(0, value_to_write.size(), 8):
self[index + 31 - (i // 8)] = Extract(i + 7, i, value_to_write)
def __getitem__(self, item: Union[int, slice]) -> Union[BitVecRef, int, list]:
if isinstance(item, slice):
start, step, stop = item.start, item.step, item.stop
if start is None:
start = 0
if stop is None: # 2**256 is just a bit too big
raise IndexError("Invalid Memory Slice")
if step is None:
step = 1
return [self[i] for i in range(start, stop, step)]
try:
return self._memory[item]
except IndexError:
return 0
def __setitem__(self, key: Union[int, slice], value: Union[BitVecRef, int, list]):
if isinstance(key, slice):
start, step, stop = key.start, key.step, key.stop
if start is None:
start = 0
if stop is None:
raise IndexError("Invalid Memory Slice")
if step is None:
step = 1
for i in range(0, stop - start, step):
self[start + i] = value[i]
else:
if isinstance(value, int):
assert 0 <= value <= 0xFF
if isinstance(value, BitVecRef):
assert value.size() == 8
self._memory[key] = value

@ -25,6 +25,8 @@ from mythril.laser.ethereum.transaction import (
from functools import reduce
from mythril.laser.ethereum.evm_exceptions import VmException
log = logging.getLogger(__name__)
class SVMError(Exception):
pass
@ -76,9 +78,7 @@ class LaserEVM:
self.pre_hooks = defaultdict(list)
self.post_hooks = defaultdict(list)
logging.info(
"LASER EVM initialized with dynamic loader: " + str(dynamic_loader)
)
log.info("LASER EVM initialized with dynamic loader: " + str(dynamic_loader))
def register_hooks(self, hook_type: str, hook_dict: Dict[str, List[Callable]]):
if hook_type == "pre":
@ -100,33 +100,33 @@ class LaserEVM:
def sym_exec(
self, main_address=None, creation_code=None, contract_name=None
) -> None:
logging.debug("Starting LASER execution")
log.debug("Starting LASER execution")
self.time = datetime.now()
if main_address:
logging.info("Starting message call transaction to {}".format(main_address))
log.info("Starting message call transaction to {}".format(main_address))
self._execute_transactions(main_address)
elif creation_code:
logging.info("Starting contract creation transaction")
log.info("Starting contract creation transaction")
created_account = execute_contract_creation(
self, creation_code, contract_name
)
logging.info(
log.info(
"Finished contract creation, found {} open states".format(
len(self.open_states)
)
)
if len(self.open_states) == 0:
logging.warning(
log.warning(
"No contract was created during the execution of contract creation "
"Increase the resources for creation execution (--max-depth or --create-timeout)"
)
self._execute_transactions(created_account.address)
logging.info("Finished symbolic execution")
logging.info(
log.info("Finished symbolic execution")
log.info(
"%d nodes, %d edges, %d total states",
len(self.nodes),
len(self.edges),
@ -138,7 +138,7 @@ class LaserEVM:
/ float(coverage[0])
* 100
)
logging.info("Achieved {:.2f}% coverage for code: {}".format(cov, code))
log.info("Achieved {:.2f}% coverage for code: {}".format(cov, code))
def _execute_transactions(self, address):
"""
@ -151,13 +151,13 @@ class LaserEVM:
initial_coverage = self._get_covered_instructions()
self.time = datetime.now()
logging.info("Starting message call transaction, iteration: {}".format(i))
log.info("Starting message call transaction, iteration: {}".format(i))
execute_message_call(self, address)
end_coverage = self._get_covered_instructions()
logging.info(
log.info(
"Number of new instructions covered in tx %d: %d"
% (i, end_coverage - initial_coverage)
)
@ -187,7 +187,7 @@ class LaserEVM:
try:
new_states, op_code = self.execute_state(global_state)
except NotImplementedError:
logging.debug("Encountered unimplemented instruction")
log.debug("Encountered unimplemented instruction")
continue
self.manage_cfg(op_code, new_states)
@ -222,9 +222,7 @@ class LaserEVM:
# In this case we don't put an unmodified world state in the open_states list Since in the case of an
# exceptional halt all changes should be discarded, and this world state would not provide us with a
# previously unseen world state
logging.debug(
"Encountered a VmException, ending path: `{}`".format(str(e))
)
log.debug("Encountered a VmException, ending path: `{}`".format(str(e)))
new_global_states = []
else:
# First execute the post hook for the transaction ending instruction
@ -388,7 +386,7 @@ class LaserEVM:
]
new_node.flags |= NodeFlags.FUNC_ENTRY
logging.debug(
log.debug(
"- Entering function "
+ environment.active_account.contract_name
+ ":"

@ -8,6 +8,8 @@ from mythril.laser.ethereum.state.environment import Environment
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.analysis.symbolic import SymExecWrapper
log = logging.getLogger(__name__)
class TaintRecord:
"""
@ -204,7 +206,7 @@ class TaintRunner:
elif op in ("CALL", "CALLCODE", "DELEGATECALL", "STATICCALL"):
TaintRunner.mutate_call(new_record, op)
else:
logging.debug("Unknown operation encountered: {}".format(op))
log.debug("Unknown operation encountered: {}".format(op))
return new_record
@ -244,7 +246,7 @@ class TaintRunner:
try:
index = helper.get_concrete_int(op0)
except TypeError:
logging.debug("Can't MLOAD taint track symbolically")
log.debug("Can't MLOAD taint track symbolically")
record.stack.append(False)
return
@ -256,7 +258,7 @@ class TaintRunner:
try:
index = helper.get_concrete_int(op0)
except TypeError:
logging.debug("Can't mstore taint track symbolically")
log.debug("Can't mstore taint track symbolically")
return
record.memory[index] = value_taint
@ -267,7 +269,7 @@ class TaintRunner:
try:
index = helper.get_concrete_int(op0)
except TypeError:
logging.debug("Can't MLOAD taint track symbolically")
log.debug("Can't MLOAD taint track symbolically")
record.stack.append(False)
return
@ -279,7 +281,7 @@ class TaintRunner:
try:
index = helper.get_concrete_int(op0)
except TypeError:
logging.debug("Can't mstore taint track symbolically")
log.debug("Can't mstore taint track symbolically")
return
record.storage[index] = value_taint

@ -1,5 +1,5 @@
from z3 import BitVec, BitVecVal
from logging import debug
import logging
from mythril.disassembler.disassembly import Disassembly
from mythril.laser.ethereum.cfg import Node, Edge, JumpType
@ -15,6 +15,7 @@ from mythril.laser.ethereum.transaction.transaction_models import (
get_next_transaction_id,
)
log = logging.getLogger(__name__)
CREATOR_ADDRESS = 0xAFFEAFFEAFFEAFFEAFFEAFFEAFFEAFFEAFFEAFFE
ATTACKER_ADDRESS = 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF
@ -28,7 +29,7 @@ def execute_message_call(laser_evm, callee_address: str) -> None:
for open_world_state in open_states:
if open_world_state[callee_address].deleted:
debug("Can not execute dead contract, skipping.")
log.debug("Can not execute dead contract, skipping.")
continue
next_transaction_id = get_next_transaction_id()

@ -1,4 +1,3 @@
import logging
from typing import Union
from mythril.disassembler.disassembly import Disassembly
from mythril.laser.ethereum.state.environment import Environment

@ -1,6 +1,5 @@
import re
from z3 import *
import logging
from typing import Union, List, Dict
import sha3 as _sha3

@ -35,7 +35,7 @@ from mythril.analysis.security import fire_lasers
from mythril.analysis.report import Report
from mythril.ethereum.interface.leveldb.client import EthLevelDB
# logging.basicConfig(level=logging.DEBUG)
log = logging.getLogger(__name__)
class Mythril(object):
@ -119,7 +119,7 @@ class Mythril(object):
if not os.path.exists(mythril_dir):
# Initialize data directory
logging.info("Creating mythril data directory")
log.info("Creating mythril data directory")
os.mkdir(mythril_dir)
db_path = str(Path(mythril_dir) / "signatures.db")
@ -154,7 +154,7 @@ class Mythril(object):
leveldb_fallback_dir = os.path.join(leveldb_fallback_dir, "geth", "chaindata")
if not os.path.exists(self.config_path):
logging.info("No config file found. Creating default: " + self.config_path)
log.info("No config file found. Creating default: " + self.config_path)
open(self.config_path, "a").close()
config = ConfigParser(allow_no_value=True)
@ -226,12 +226,12 @@ class Mythril(object):
"Could not extract solc version from string {}".format(main_version)
)
if version == main_version_number:
logging.info("Given version matches installed version")
log.info("Given version matches installed version")
solc_binary = os.environ.get("SOLC") or "solc"
else:
solc_binary = util.solc_exists(version)
if solc_binary:
logging.info("Given version is already installed")
log.info("Given version is already installed")
else:
try:
solc.install_solc("v" + version)
@ -243,7 +243,7 @@ class Mythril(object):
"There was an error when trying to install the specified solc version"
)
logging.info("Setting the compiler to %s", solc_binary)
log.info("Setting the compiler to %s", solc_binary)
return solc_binary
@ -254,7 +254,7 @@ class Mythril(object):
def set_api_rpc_infura(self):
self.eth = EthJsonRpc("mainnet.infura.io", 443, True)
logging.info("Using INFURA for RPC queries")
log.info("Using INFURA for RPC queries")
def set_api_rpc(self, rpc=None, rpctls=False):
if rpc == "ganache":
@ -274,13 +274,13 @@ class Mythril(object):
if rpcconfig:
self.eth = EthJsonRpc(rpcconfig[0], int(rpcconfig[1]), rpcconfig[2])
logging.info("Using RPC settings: %s" % str(rpcconfig))
log.info("Using RPC settings: %s" % str(rpcconfig))
else:
raise CriticalError("Invalid RPC settings, check help for details.")
def set_api_rpc_localhost(self):
self.eth = EthJsonRpc("localhost", 8545)
logging.info("Using default RPC settings: http://localhost:8545")
log.info("Using default RPC settings: http://localhost:8545")
def set_api_from_config_path(self):
config = ConfigParser(allow_no_value=False)
@ -408,7 +408,7 @@ class Mythril(object):
except CompilerError as e:
raise CriticalError(e)
except NoContractFoundError:
logging.error(
log.error(
"The file " + file + " does not contain a compilable contract."
)

@ -2,6 +2,8 @@ from mythril.disassembler.disassembly import Disassembly
import logging
import re
log = logging.getLogger(__name__)
class DynLoader:
def __init__(self, eth, contract_loading=True, storage_loading=True):
@ -46,9 +48,7 @@ class DynLoader:
if not self.contract_loading:
raise ValueError("Cannot load contract when contract_loading flag is false")
logging.debug(
"Dynld at contract " + contract_address + ": " + dependency_address
)
log.debug("Dynld at contract " + contract_address + ": " + dependency_address)
m = re.match(r"^(0x[0-9a-fA-F]{40})$", dependency_address)
@ -58,7 +58,7 @@ class DynLoader:
else:
return None
logging.debug("Dependency address: " + dependency_address)
log.debug("Dependency address: " + dependency_address)
code = self.eth.eth_getCode(dependency_address)

@ -15,6 +15,8 @@ from subprocess import Popen, PIPE
from mythril.exceptions import CompilerError
log = logging.getLogger(__name__)
lock = multiprocessing.Lock()
@ -90,7 +92,7 @@ class SignatureDB(object, metaclass=Singleton):
)
self.path = os.path.join(self.path, "signatures.db")
logging.info("Using signature database at %s", self.path)
log.info("Using signature database at %s", self.path)
# NOTE: Creates a new DB file if it doesn't exist already
with SQLiteDB(self.path) as cur:
cur.execute(
@ -185,7 +187,7 @@ class SignatureDB(object, metaclass=Singleton):
except FourByteDirectoryOnlineLookupError as fbdole:
# wait at least 2 mins to try again
self.online_lookup_timeout = time.time() + 2 * 60
logging.warning("Online lookup failed, not retrying for 2min: %s", fbdole)
log.warning("Online lookup failed, not retrying for 2min: %s", fbdole)
return []
def import_solidity_file(
@ -226,8 +228,7 @@ class SignatureDB(object, metaclass=Singleton):
solc_bytes = "0x" + line.split(":")[0]
solc_text = line.split(":")[1].strip()
self.solidity_sigs[solc_bytes].append(solc_text)
logging.debug(
log.debug(
"Signatures: found %d signatures after parsing" % len(self.solidity_sigs)
)

@ -14,6 +14,8 @@ from mythril.analysis.report import Report
from mythril.ethereum import util
from mythril.laser.ethereum.util import get_instruction_index
log = logging.getLogger(__name__)
def analyze_truffle_project(sigs, args):
@ -105,7 +107,7 @@ def analyze_truffle_project(sigs, args):
].decode("utf-8")
issue.lineno = mappings[index].lineno
except IndexError:
logging.debug("No code mapping at index %d", index)
log.debug("No code mapping at index %d", index)
report.append_issue(issue)

@ -1,7 +1,8 @@
coloredlogs>=10.0
configparser>=3.5.0
coverage
eth_abi>=1.0.0
py_ecc==1.4.2
eth_abi==1.3.0
eth-account>=0.1.0a2
ethereum>=2.3.2
ethereum-input-decoder>=0.2.2
@ -10,7 +11,7 @@ eth-keyfile>=0.5.1
eth-keys>=0.2.0b3
eth-rlp>=0.1.0
eth-tester>=0.1.0b21
eth-typing<2.0.0,>=1.3.0
eth-typing>=2.0.0
eth-utils>=1.0.1
jinja2>=2.9
mock

@ -74,12 +74,13 @@ setup(
packages=find_packages(exclude=["contrib", "docs", "tests"]),
install_requires=[
"coloredlogs>=10.0",
"py_ecc==1.4.2",
"ethereum>=2.3.2",
"z3-solver>=4.8.0.0",
"requests",
"py-solc",
"plyvel",
"eth_abi>=1.0.0",
"eth_abi==1.3.0",
"eth-utils>=1.0.1",
"eth-account>=0.1.0a2",
"eth-hash>=0.1.0",
@ -87,7 +88,7 @@ setup(
"eth-keys>=0.2.0b3",
"eth-rlp>=0.1.0",
"eth-tester>=0.1.0b21",
"eth-typing>=1.3.0,<2.0.0",
"eth-typing>=2.0.0",
"coverage",
"jinja2>=2.9",
"rlp>=1.0.1",

@ -1,6 +1,9 @@
import pytest
from z3 import BitVec, simplify
from mythril.laser.ethereum.state.machine_state import MachineState
from mythril.laser.ethereum.evm_exceptions import StackUnderflowException
from mythril.laser.ethereum.state.memory import Memory
memory_extension_test_data = [(0, 0, 10), (0, 30, 10), (100, 22, 8)]
@ -11,7 +14,8 @@ memory_extension_test_data = [(0, 0, 10), (0, 30, 10), (100, 22, 8)]
def test_memory_extension(initial_size, start, extension_size):
# Arrange
machine_state = MachineState(gas_limit=8000000)
machine_state.memory = [0] * initial_size
machine_state.memory = Memory()
machine_state.memory.extend(initial_size)
# Act
machine_state.mem_extend(start, extension_size)
@ -81,18 +85,39 @@ def test_stack_single_pop():
assert isinstance(result, int)
memory_write_test_data = [(5, 10, [1, 2, 3]), (0, 0, [3, 4]), (20, 1, [2, 4, 10])]
def test_memory_zeroed():
# Arrange
mem = Memory()
mem.extend(2000 + 32)
# Act
mem[11] = 10
mem.write_word_at(2000, 0x12345)
# Assert
assert mem[10] == 0
assert mem[100] == 0
assert mem.get_word_at(1000) == 0
@pytest.mark.parametrize("initial_size, memory_offset, data", memory_write_test_data)
def test_memory_write(initial_size, memory_offset, data):
def test_memory_write():
# Arrange
machine_state = MachineState(8000000)
machine_state.memory = [0] * initial_size
mem = Memory()
mem.extend(200 + 32)
a = BitVec("a", 256)
b = BitVec("b", 8)
# Act
machine_state.memory_write(memory_offset, data)
mem[11] = 10
mem[12] = b
mem.write_word_at(200, 0x12345)
mem.write_word_at(100, a)
# Assert
assert len(machine_state.memory) == max(initial_size, memory_offset + len(data))
assert machine_state.memory[memory_offset : memory_offset + len(data)] == data
assert mem[0] == 0
assert mem[11] == 10
assert mem[200 + 31] == 0x45
assert mem.get_word_at(200) == 0x12345
assert simplify(a == mem.get_word_at(100))
assert simplify(b == mem[12])

@ -14,11 +14,15 @@ contract Caller {
constructor (address addr) public {
fixedAddress = addr;
}
/*
// Commented out because this causes laser to enter an infinite loop... :/
// It sets the free memory pointer to a symbolic value, and things break
//some typical function as a decoy
function thisisfine() public {
(bool success, bytes memory mem) = fixedAddress.call("");
}
*/
function sha256Test1() public returns (uint256) {
uint256 i;

@ -1 +1,66 @@
{"error": null, "issues": [{"address": 618, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The function `_function_0x141f32ff` uses callcode. Callcode does not persist sender or value over the call. Use delegatecall instead.", "function": "_function_0x141f32ff", "max_gas_used": 1141, "min_gas_used": 389, "swc-id": "111", "title": "Use of callcode", "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, "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": 35919, "min_gas_used": 1167, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}, {"address": 1038, "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": "_function_0xeea4c864", "max_gas_used": 1229, "min_gas_used": 477, "swc-id": "107", "title": "External call to user-supplied address", "type": "Warning"}, {"address": 1046, "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": 35944, "min_gas_used": 1192, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}], "success": true}
{
"error": null,
"issues": [
{
"address": 618,
"contract": "Unknown",
"debug": "<DEBUG-DATA>",
"description": "The function `_function_0x141f32ff` uses callcode. Callcode does not persist sender or value over the call. Use delegatecall instead.",
"function": "_function_0x141f32ff",
"max_gas_used": 1141,
"min_gas_used": 389,
"swc-id": "111",
"title": "Use of callcode",
"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,
"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,
"swc-id": "104",
"title": "Unchecked CALL return value",
"type": "Informational"
},
{
"address": 1038,
"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": "_function_0xeea4c864",
"max_gas_used": 1223,
"min_gas_used": 471,
"swc-id": "107",
"title": "External call to user-supplied address",
"type": "Warning"
},
{
"address": 1046,
"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,
"swc-id": "104",
"title": "Unchecked CALL return value",
"type": "Informational"
}
],
"success": true
}

@ -30,7 +30,7 @@ The return value of an external call is not checked. Note that execution continu
- Contract: Unknown
- Function name: `_function_0x9b58bc26`
- PC address: 857
- Estimated Gas Usage: 1167 - 35919
- Estimated Gas Usage: 1161 - 35913
### Description
@ -42,7 +42,7 @@ The return value of an external call is not checked. Note that execution continu
- Contract: Unknown
- Function name: `_function_0xeea4c864`
- PC address: 1038
- Estimated Gas Usage: 477 - 1229
- Estimated Gas Usage: 471 - 1223
### Description
@ -54,7 +54,7 @@ The contract executes a function call with high gas to a user-supplied address.
- Contract: Unknown
- Function name: `_function_0xeea4c864`
- PC address: 1046
- Estimated Gas Usage: 1192 - 35944
- Estimated Gas Usage: 1186 - 35938
### Description

@ -24,7 +24,7 @@ Type: Informational
Contract: Unknown
Function name: _function_0x9b58bc26
PC address: 857
Estimated Gas Usage: 1167 - 35919
Estimated Gas Usage: 1161 - 35913
The return value of an external call is not checked. Note that execution continue even if the called contract throws.
--------------------
@ -34,7 +34,7 @@ Type: Warning
Contract: Unknown
Function name: _function_0xeea4c864
PC address: 1038
Estimated Gas Usage: 477 - 1229
Estimated Gas Usage: 471 - 1223
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.
--------------------
@ -44,7 +44,7 @@ Type: Informational
Contract: Unknown
Function name: _function_0xeea4c864
PC address: 1046
Estimated Gas Usage: 1192 - 35944
Estimated Gas Usage: 1186 - 35938
The return value of an external call is not checked. Note that execution continue even if the called contract throws.
--------------------

Loading…
Cancel
Save