Fix merge conflicts

feature/regioned_storage
Nikhil 5 years ago
commit b29690c361
  1. 16
      all_tests.sh
  2. 2
      mythril/__version__.py
  3. 30
      mythril/analysis/analysis_args.py
  4. 5
      mythril/analysis/modules/deprecated_ops.py
  5. 3
      mythril/analysis/modules/dos.py
  6. 4
      mythril/analysis/modules/state_change_external_calls.py
  7. 26
      mythril/analysis/report.py
  8. 7
      mythril/analysis/solver.py
  9. 2
      mythril/analysis/symbolic.py
  10. 2
      mythril/analysis/templates/report_as_markdown.jinja2
  11. 2
      mythril/analysis/templates/report_as_text.jinja2
  12. 6
      mythril/disassembler/disassembly.py
  13. 13
      mythril/interfaces/cli.py
  14. 7
      mythril/interfaces/old_cli.py
  15. 14
      mythril/laser/ethereum/state/account.py
  16. 7
      mythril/laser/smt/bitvecfunc.py
  17. 5
      mythril/mythril/mythril_analyzer.py
  18. 52
      mythril/support/loader.py

@ -7,22 +7,6 @@ assert sys.version_info[0:2] >= (3,5), \
"""Please make sure you are using Python 3.5 or later.
You ran with {}""".format(sys.version)' || exit $?
echo "Checking solc version..."
out=$(solc --version) || {
echo 2>&1 "Please make sure you have solc installed, version 0.4.21 or greater"
}
case $out in
*Version:\ 0.4.2[1-9]* )
echo $out
;;
* )
echo $out
echo "Please make sure your solc version is at least 0.4.21"
exit 1
;;
esac
echo "Checking that truffle is installed..."
if ! which truffle ; then
echo "Please make sure you have etherum truffle installed (npm install -g truffle)"

@ -4,4 +4,4 @@ This file is suitable for sourcing inside POSIX shell, e.g. bash as well
as for importing into Python.
"""
__version__ = "v0.21.10"
__version__ = "v0.21.12"

@ -0,0 +1,30 @@
from mythril.support.support_utils import Singleton
class AnalysisArgs(object, metaclass=Singleton):
"""
This module helps in preventing args being sent through multiple of classes to reach analysis modules
"""
def __init__(self):
self._loop_bound = 3
self._solver_timeout = 10000
def set_loop_bound(self, loop_bound: int):
if loop_bound is not None:
self._loop_bound = loop_bound
def set_solver_timeout(self, solver_timeout: int):
if solver_timeout is not None:
self._solver_timeout = solver_timeout
@property
def loop_bound(self):
return self._loop_bound
@property
def solver_timeout(self):
return self._solver_timeout
analysis_args = AnalysisArgs()

@ -35,6 +35,7 @@ class DeprecatedOperationsModule(DetectionModule):
if state.get_current_instruction()["address"] in self._cache:
return
issues = self._analyze_state(state)
for issue in issues:
self._cache.add(issue.address)
self._issues.extend(issues)
@ -74,13 +75,13 @@ class DeprecatedOperationsModule(DetectionModule):
)
swc_id = DEPRECATED_FUNCTIONS_USAGE
else:
return
return []
try:
transaction_sequence = get_transaction_sequence(
state, state.mstate.constraints
)
except UnsatError:
return
return []
issue = Issue(
contract=state.environment.active_account.contract_name,
function_name=state.environment.active_function_name,

@ -7,6 +7,7 @@ from mythril.analysis.swc_data import DOS_WITH_BLOCK_GAS_LIMIT
from mythril.analysis.report import Issue
from mythril.analysis.modules.base import DetectionModule
from mythril.analysis.solver import get_transaction_sequence, UnsatError
from mythril.analysis.analysis_args import analysis_args
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.laser.ethereum.state.annotation import StateAnnotation
from mythril.laser.ethereum import util
@ -90,7 +91,7 @@ class DosModule(DetectionModule):
else:
annotation.jump_targets[target] = 1
if annotation.jump_targets[target] > 2:
if annotation.jump_targets[target] > min(2, analysis_args.loop_bound - 1):
annotation.loop_start = address
elif annotation.loop_start is not None:

@ -171,7 +171,9 @@ class StateChange(DetectionModule):
for annotation in annotations:
if not annotation.state_change_states:
continue
vulnerabilities.append(annotation.get_issue(global_state))
issue = annotation.get_issue(global_state)
if issue:
vulnerabilities.append(issue)
return vulnerabilities
@staticmethod

@ -11,6 +11,7 @@ from mythril.analysis.swc_data import SWC_TO_TITLE
from mythril.support.source_support import Source
from mythril.support.start_time import StartTime
from mythril.support.support_utils import get_code_hash
from mythril.support.signatures import SignatureDB
from time import time
log = logging.getLogger(__name__)
@ -151,6 +152,30 @@ class Issue:
else:
self.source_mapping = self.address
def resolve_function_names(self):
""" Resolves function names for each step """
if (
self.transaction_sequence is None
or "steps" not in self.transaction_sequence
):
return
signatures = SignatureDB()
for step in self.transaction_sequence["steps"]:
_hash = step["input"][:10]
try:
sig = signatures.get(_hash)
if len(sig) > 0:
step["name"] = sig[0]
else:
step["name"] = "unknown"
except ValueError:
step["name"] = "unknown"
class Report:
"""A report containing the content of multiple issues."""
@ -187,6 +212,7 @@ class Report:
"""
m = hashlib.md5()
m.update((issue.contract + str(issue.address) + issue.title).encode("utf-8"))
issue.resolve_function_names()
self.issues[m.digest()] = issue
def as_text(self):

@ -4,6 +4,7 @@ from typing import Dict, Tuple, Union
from z3 import sat, unknown, FuncInterp
import z3
from mythril.analysis.analysis_args import analysis_args
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.laser.ethereum.state.constraints import Constraints
from mythril.laser.ethereum.transaction import BaseTransaction
@ -29,7 +30,7 @@ def get_model(constraints, minimize=(), maximize=(), enforce_execution_time=True
:return:
"""
s = Optimize()
timeout = 100000
timeout = analysis_args.solver_timeout
if enforce_execution_time:
timeout = min(timeout, time_handler.time_remaining() - 500)
if timeout <= 0:
@ -132,8 +133,8 @@ def _get_concrete_state(initial_accounts: Dict, min_price_dict: Dict[str, int]):
data = dict() # type: Dict[str, Union[int, str]]
data["nonce"] = account.nonce
data["code"] = account.code.bytecode
data["storage"] = str(account.storage)
data["balance"] = min_price_dict.get(address, 0)
data["storage"] = account.storage.printable_storage
data["balance"] = hex(min_price_dict.get(address, 0))
accounts[hex(address)] = data
return {"accounts": accounts}

@ -47,7 +47,7 @@ class SymExecWrapper:
dynloader=None,
max_depth=22,
execution_timeout=None,
loop_bound=2,
loop_bound=3,
create_timeout=None,
transaction_count=2,
modules=(),

@ -32,7 +32,7 @@ In file: {{ issue.filename }}:{{ issue.lineno }}
{% if step == issue.tx_sequence.steps[0] and step.input != "0x" and step.origin == "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe" %}
Caller: [CREATOR], data: [CONTRACT CREATION], value: {{ step.value }}
{% else %}
Caller: {% if step.origin == "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe" %}[CREATOR]{% elif step.origin == "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef" %}[ATTACKER]{% else %}[SOMEGUY]{% endif %}, data: {{ step.input }}, value: {{ step.value }}
Caller: {% if step.origin == "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe" %}[CREATOR]{% elif step.origin == "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef" %}[ATTACKER]{% else %}[SOMEGUY]{% endif %}, function: {{ step.name }}, txdata: {{ step.input }}, value: {{ step.value }}
{% endif %}
{% endfor %}
{% endif %}

@ -25,7 +25,7 @@ Transaction Sequence:
{% if step == issue.tx_sequence.steps[0] and step.input != "0x" and step.origin == "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe" %}
Caller: [CREATOR], data: [CONTRACT CREATION], value: {{ step.value }}
{% else %}
Caller: {% if step.origin == "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe" %}[CREATOR]{% elif step.origin == "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef" %}[ATTACKER]{% else %}[SOMEGUY]{% endif %}, data: {{ step.input }}, value: {{ step.value }}
Caller: {% if step.origin == "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe" %}[CREATOR]{% elif step.origin == "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef" %}[ATTACKER]{% else %}[SOMEGUY]{% endif %}, function: {{ step.name }}, txdata: {{ step.input }}, value: {{ step.value }}
{% endif %}
{% endfor %}
{% endif %}

@ -84,10 +84,8 @@ def get_function_info(
# Append with missing 0s at the beginning
function_hash = "0x" + instruction_list[index]["argument"][2:].rjust(8, "0")
function_names = signature_database.get(function_hash)
if len(function_names) > 1:
# In this case there was an ambiguous result
function_name = "[{}] (ambiguous)".format(", ".join(function_names))
elif len(function_names) == 1:
if len(function_names) > 0:
function_name = function_names[0]
else:
function_name = "_function_" + function_hash

@ -346,11 +346,17 @@ def create_analyzer_parser(analyzer_parser: ArgumentParser):
default=86400,
help="The amount of seconds to spend on symbolic execution",
)
options.add_argument(
"--solver-timeout",
type=int,
default=10000,
help="The maximum amount of time(in milli seconds) the solver spends for queries from analysis modules",
)
options.add_argument(
"--create-timeout",
type=int,
default=10,
help="The amount of seconds to spend on " "the initial contract creation",
help="The amount of seconds to spend on the initial contract creation",
)
options.add_argument(
"-l",
@ -360,8 +366,9 @@ def create_analyzer_parser(analyzer_parser: ArgumentParser):
)
options.add_argument(
"--no-onchain-storage-access",
"--no-onchain-access",
action="store_true",
help="turns off getting the data from onchain contracts",
help="turns off getting the data from onchain contracts (both loading storage and contract code)",
)
options.add_argument(
@ -552,6 +559,8 @@ def execute_command(
enable_iprof=args.enable_iprof,
disable_dependency_pruning=args.disable_dependency_pruning,
onchain_storage_access=not args.no_onchain_storage_access,
solver_timeout=args.solver_timeout,
requires_dynld=not args.no_onchain_storage_access,
)
if not disassembler.contracts:

@ -213,6 +213,12 @@ def create_parser(parser: argparse.ArgumentParser) -> None:
default=2,
help="Maximum number of transactions issued by laser",
)
options.add_argument(
"--solver-timeout",
type=int,
default=10000,
help="The maximum amount of time(in milli seconds) the solver spends for queries from analysis modules",
)
options.add_argument(
"--execution-timeout",
type=int,
@ -419,6 +425,7 @@ def execute_command(
enable_iprof=args.enable_iprof,
disable_dependency_pruning=args.disable_dependency_pruning,
onchain_storage_access=not args.no_onchain_storage_access,
solver_timeout=args.solver_timeout,
)
if args.disassemble:

@ -21,8 +21,16 @@ from mythril.disassembler.disassembly import Disassembly
from mythril.laser.smt import symbol_factory
class ArrayStorageRegion:
""" An ArrayStorageRegion is a storage region that leverages smt array theory to resolve expressions"""
class StorageRegion:
def __getitem__(self, item):
raise NotImplementedError
def __setitem__(self, key, value):
raise NotImplementedError
class ArrayStorageRegion(StorageRegion):
"""Storage class represents the storage of an Account."""
def __init__(self, concrete=False, address=None, dynamic_loader=None) -> None:
"""Constructor for Storage.
@ -130,7 +138,7 @@ class ArrayStorageRegion:
return str(self.printable_storage)
class IteStorageRegion:
class IteStorageRegion(StorageRegion):
""" An IteStorageRegion is a storage region that uses Ite statements to implement a storage"""
def __init__(self) -> None:

@ -1,7 +1,6 @@
import operator
from itertools import product
from typing import Optional, Union, cast, Callable, List
import z3
from mythril.laser.smt.bitvec import BitVec, Annotations
@ -63,7 +62,11 @@ def _comparison_helper(
union = a.annotations.union(b.annotations)
if not a.symbolic and not b.symbolic:
return Bool(z3.BoolVal(operation(a.raw, b.raw)), annotations=union)
if operation == z3.UGT:
operation = operator.gt
if operation == z3.ULT:
operation = operator.lt
return Bool(z3.BoolVal(operation(a.value, b.value)), annotations=union)
if (
not isinstance(b, BitVecFunc)
or not a.func_name

@ -10,6 +10,7 @@ from mythril.support.source_support import Source
from mythril.support.loader import DynLoader
from mythril.analysis.symbolic import SymExecWrapper
from mythril.analysis.callgraph import generate_graph
from mythril.analysis.analysis_args import analysis_args
from mythril.analysis.traceexplore import get_serializable_statespace
from mythril.analysis.security import fire_lasers, retrieve_callback_issues
from mythril.analysis.report import Report, Issue
@ -39,6 +40,7 @@ class MythrilAnalyzer:
create_timeout: Optional[int] = None,
enable_iprof: bool = False,
disable_dependency_pruning: bool = False,
solver_timeout: Optional[int] = None,
):
"""
@ -60,6 +62,9 @@ class MythrilAnalyzer:
self.enable_iprof = enable_iprof
self.disable_dependency_pruning = disable_dependency_pruning
analysis_args.set_loop_bound(loop_bound)
analysis_args.set_solver_timeout(solver_timeout)
def dump_statespace(self, contract: EVMContract = None) -> str:
"""
Returns serializable statespace of the contract

@ -3,6 +3,11 @@ and dependencies."""
from mythril.disassembler.disassembly import Disassembly
import logging
import re
import functools
from mythril.ethereum.interface.rpc.client import EthJsonRpc
from typing import Optional
LRU_CACHE_SIZE = 4096
log = logging.getLogger(__name__)
@ -10,7 +15,9 @@ log = logging.getLogger(__name__)
class DynLoader:
"""The dynamic loader class."""
def __init__(self, eth, contract_loading=True, storage_loading=True):
def __init__(
self, eth: Optional[EthJsonRpc], contract_loading=True, storage_loading=True
):
"""
:param eth:
@ -18,11 +25,11 @@ class DynLoader:
:param storage_loading:
"""
self.eth = eth
self.storage_cache = {}
self.contract_loading = contract_loading
self.storage_loading = storage_loading
def read_storage(self, contract_address: str, index: int):
@functools.lru_cache(LRU_CACHE_SIZE)
def read_storage(self, contract_address: str, index: int) -> str:
"""
:param contract_address:
@ -30,43 +37,28 @@ class DynLoader:
:return:
"""
if not self.storage_loading:
raise Exception(
raise ValueError(
"Cannot load from the storage when the storage_loading flag is false"
)
if not self.eth:
raise ValueError("Cannot load from the storage when eth is None")
try:
contract_ref = self.storage_cache[contract_address]
data = contract_ref[index]
except KeyError:
self.storage_cache[contract_address] = {}
data = self.eth.eth_getStorageAt(
contract_address, position=index, block="latest"
)
self.storage_cache[contract_address][index] = data
except IndexError:
data = self.eth.eth_getStorageAt(
contract_address, position=index, block="latest"
)
self.storage_cache[contract_address][index] = data
return data
return self.eth.eth_getStorageAt(
contract_address, position=index, block="latest"
)
def dynld(self, dependency_address):
@functools.lru_cache(LRU_CACHE_SIZE)
def dynld(self, dependency_address: str) -> Optional[Disassembly]:
"""
:param dependency_address:
:return:
"""
if not self.contract_loading:
raise ValueError("Cannot load contract when contract_loading flag is false")
if not self.eth:
raise ValueError("Cannot load from the storage when eth is None")
log.debug("Dynld at contract " + dependency_address)
log.debug("Dynld at contract %s", dependency_address)
# Ensure that dependency_address is the correct length, with 0s prepended as needed.
dependency_address = (
@ -81,7 +73,7 @@ class DynLoader:
else:
return None
log.debug("Dependency address: " + dependency_address)
log.debug("Dependency address: %s", dependency_address)
code = self.eth.eth_getCode(dependency_address)

Loading…
Cancel
Save