Merge remote-tracking branch 'origin/develop' into features/pluginloader

pull/980/head
Joran Honig 6 years ago
commit e1f982c28f
  1. 2
      .gitignore
  2. 5
      README.md
  3. 2
      mythril/analysis/modules/integer.py
  4. 31
      mythril/analysis/report.py
  5. 2
      mythril/analysis/security.py
  6. 2
      mythril/analysis/symbolic.py
  7. 4
      mythril/disassembler/asm.py
  8. 4
      mythril/ethereum/interface/leveldb/state.py
  9. 390
      mythril/interfaces/cli.py
  10. 8
      mythril/laser/ethereum/gas.py
  11. 61
      mythril/laser/ethereum/instructions.py
  12. 5
      mythril/laser/ethereum/state/memory.py
  13. 1
      mythril/laser/ethereum/taint_analysis.py
  14. 1
      mythril/laser/smt/__init__.py
  15. 38
      mythril/laser/smt/bitvec.py
  16. 16
      mythril/laser/smt/bitvecfunc.py
  17. 5
      mythril/mythril.py
  18. 4
      mythril/mythril/__init__.py
  19. 173
      mythril/mythril/mythril_analyzer.py
  20. 226
      mythril/mythril/mythril_config.py
  21. 303
      mythril/mythril/mythril_disassembler.py
  22. 49
      mythril/mythril/mythril_leveldb.py
  23. 15
      mythril/solidity/soliditycontract.py
  24. 94
      mythril/support/opcodes.py
  25. 18
      mythril/support/truffle.py
  26. 2
      mythril/version.py
  27. 2
      solidity_examples/WalletLibrary.sol
  28. 10
      solidity_examples/rubixi.sol
  29. 15
      tests/graph_test.py
  30. 148
      tests/instructions/sar_test.py
  31. 123
      tests/instructions/shl_test.py
  32. 125
      tests/instructions/shr_test.py
  33. 6
      tests/laser/transaction/create_transaction_test.py
  34. 31
      tests/mythril/mythril_analyzer_test.py
  35. 58
      tests/mythril/mythril_config_test.py
  36. 70
      tests/mythril/mythril_disassembler_test.py
  37. 51
      tests/mythril/mythril_leveldb_test.py
  38. 5
      tests/native_test.py
  39. 11
      tests/report_test.py
  40. 8
      tests/solidity_contract_test.py
  41. 2
      tests/testdata/mythril_config_inputs/config.ini
  42. 290
      tests/testdata/outputs_expected/calls.sol.o.jsonv2
  43. 16
      tests/testdata/outputs_expected/exceptions.sol.o.jsonv2
  44. 12
      tests/testdata/outputs_expected/kinds_of_calls.sol.o.json
  45. 164
      tests/testdata/outputs_expected/kinds_of_calls.sol.o.jsonv2
  46. 6
      tests/testdata/outputs_expected/kinds_of_calls.sol.o.markdown
  47. 6
      tests/testdata/outputs_expected/kinds_of_calls.sol.o.text
  48. 4
      tests/testdata/outputs_expected/multi_contracts.sol.o.jsonv2
  49. 4
      tests/testdata/outputs_expected/origin.sol.o.jsonv2
  50. 8
      tests/testdata/outputs_expected/overflow.sol.o.json
  51. 8
      tests/testdata/outputs_expected/overflow.sol.o.jsonv2
  52. 4
      tests/testdata/outputs_expected/overflow.sol.o.markdown
  53. 4
      tests/testdata/outputs_expected/overflow.sol.o.text
  54. 12
      tests/testdata/outputs_expected/returnvalue.sol.o.jsonv2
  55. 52
      tests/testdata/outputs_expected/suicide.sol.o.jsonv2
  56. 8
      tests/testdata/outputs_expected/underflow.sol.o.json
  57. 8
      tests/testdata/outputs_expected/underflow.sol.o.jsonv2
  58. 4
      tests/testdata/outputs_expected/underflow.sol.o.markdown
  59. 4
      tests/testdata/outputs_expected/underflow.sol.o.text
  60. 2
      tox.ini

2
.gitignore vendored

@ -175,7 +175,7 @@ lol*
coverage_html_report/
tests/testdata/outputs_current/
tests/testdata/outputs_current_laser_result/
tests/mythril_dir/signatures.db
tests/testdata/mythril_config_inputs/config.ini
# VSCode
.vscode

@ -14,7 +14,10 @@
Mythril Classic is an open-source security analysis tool for Ethereum smart contracts. It uses symbolic analysis, taint analysis and control flow checking to detect a variety of security vulnerabilities.
This is an experimental tool designed for security guys. If you a smart contract developer you might prefer [Truffle Security](https://github.com/ConsenSys/truffle-security) or other convenient tools built on the [MythX API](https://mythx.io).
Note that Mythril Classic is designed for security auditors. If you are a smart contract developer, we recommend using [MythX tools](https://github.com/b-mueller/awesome-mythx-smart-contract-security) which are optimized for usability and cover a wider range of security issues:
- [Sabre](https://github.com/b-mueller/sabre)
- [MythX Plugin for Truffle](https://github.com/ConsenSys/truffle-security)
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.

@ -290,7 +290,7 @@ class IntegerOverflowUnderflowModule(DetectionModule):
try:
transaction_sequence = solver.get_transaction_sequence(
state, node.constraints + [annotation.constraint]
state, state.mstate.constraints + [annotation.constraint]
)
except UnsatError:
continue

@ -3,6 +3,7 @@ import logging
import json
import operator
from jinja2 import PackageLoader, Environment
from typing import Dict, List
import _pysha3 as sha3
import hashlib
@ -106,6 +107,17 @@ class Issue:
return issue
def _set_internal_compiler_error(self):
"""
Adds the false positive to description and changes severity to low
"""
self.severity = "Low"
self.description_tail += (
" This issue is reported for internal compiler generated code."
)
self.description = "%s\n%s" % (self.description_head, self.description_tail)
self.code = ""
def add_code_info(self, contract):
"""
@ -118,6 +130,8 @@ class Issue:
self.filename = codeinfo.filename
self.code = codeinfo.code
self.lineno = codeinfo.lineno
if self.lineno is None:
self._set_internal_compiler_error()
self.source_mapping = codeinfo.solc_mapping
else:
self.source_mapping = self.address
@ -130,7 +144,7 @@ class Report:
loader=PackageLoader("mythril.analysis"), trim_blocks=True
)
def __init__(self, verbose=False, source=None):
def __init__(self, verbose=False, source=None, exceptions=None):
"""
:param verbose:
@ -140,6 +154,7 @@ class Report:
self.solc_version = ""
self.meta = {}
self.source = source or Source()
self.exceptions = exceptions or []
def sorted_issues(self):
"""
@ -177,6 +192,14 @@ class Report:
result = {"success": True, "error": None, "issues": self.sorted_issues()}
return json.dumps(result, sort_keys=True)
def _get_exception_data(self) -> dict:
if not self.exceptions:
return {}
logs = [] # type: List[Dict]
for exception in self.exceptions:
logs += [{"level": "error", "hidden": "true", "error": exception}]
return {"logs": logs}
def as_swc_standard_format(self):
"""Format defined for integration and correlation.
@ -208,17 +231,17 @@ class Report:
},
"severity": issue.severity,
"locations": [{"sourceMap": "%d:1:%d" % (issue.address, idx)}],
"extra": {},
"extra": {"discoveryTime": int(issue.discovery_time * 10 ** 9)},
}
)
meta_data = self._get_exception_data()
result = [
{
"issues": _issues,
"sourceType": "raw-bytecode",
"sourceFormat": "evm-byzantium-bytecode",
"sourceList": source_list,
"meta": {},
"meta": meta_data,
}
]

@ -1,7 +1,7 @@
"""This module contains functionality for hooking in detection modules and
executing them."""
from collections import defaultdict
from ethereum.opcodes import opcodes
from mythril.support.opcodes import opcodes
from mythril.analysis import modules
import pkgutil
import importlib.util

@ -193,7 +193,7 @@ class SymExecWrapper:
)
elif op == "SSTORE":
stack = copy.deepcopy(state.mstate.stack)
stack = copy.copy(state.mstate.stack)
address = state.environment.active_account.address
index, value = stack.pop(), stack.pop()

@ -4,12 +4,12 @@ code disassembly."""
import re
from collections import Generator
from ethereum.opcodes import opcodes
from mythril.support.opcodes import opcodes
regex_PUSH = re.compile(r"^PUSH(\d*)$")
# Additional mnemonic to catch failed assertions
opcodes[254] = ["ASSERT_FAIL", 0, 0, 0]
opcodes[254] = ("ASSERT_FAIL", 0, 0, 0)
class EvmInstruction:

@ -150,7 +150,7 @@ class State:
rlpdata = self.trie.get(addr)
if rlpdata != trie.BLANK_NODE:
o = rlp.decode(rlpdata, Account, db=self.db, address=addr)
o = rlp.decode(rlpdata, Account, db=self.db, addr=addr)
else:
o = Account.blank_account(self.db, addr, 0)
self.cache[addr] = o
@ -162,4 +162,4 @@ class State:
"""iterates through trie to and yields non-blank leafs as accounts."""
for address_hash, rlpdata in self.secure_trie.trie.iter_branch():
if rlpdata != trie.BLANK_NODE:
yield rlp.decode(rlpdata, Account, db=self.db, address=address_hash)
yield rlp.decode(rlpdata, Account, db=self.db, addr=address_hash)

@ -12,10 +12,16 @@ import os
import sys
import coloredlogs
import traceback
import mythril.support.signatures as sigs
from mythril.exceptions import AddressNotFoundError, CriticalError
from mythril.mythril import Mythril
from mythril.mythril import (
MythrilAnalyzer,
MythrilDisassembler,
MythrilConfig,
MythrilLevelDB,
)
from mythril.version import VERSION
# logging.basicConfig(level=logging.DEBUG)
@ -25,23 +31,48 @@ log = logging.getLogger(__name__)
def exit_with_error(format_, message):
"""
:param format_:
:param message:
"""
if format_ == "text" or format_ == "markdown":
log.error(message)
else:
elif format_ == "json":
result = {"success": False, "error": str(message), "issues": []}
print(json.dumps(result))
else:
result = [
{
"issues": [],
"sourceType": "",
"sourceFormat": "",
"sourceList": [],
"meta": {
"logs": [{"level": "error", "hidden": "true", "error": message}]
},
}
]
print(json.dumps(result))
sys.exit()
def main():
def main() -> None:
"""The main CLI interface entry point."""
parser = argparse.ArgumentParser(
description="Security analysis of Ethereum smart contracts"
)
create_parser(parser)
# Get config values
args = parser.parse_args()
parse_args(parser=parser, args=args)
def create_parser(parser: argparse.ArgumentParser) -> None:
"""
Creates the parser by setting all the possible arguments
:param parser: The parser
"""
parser.add_argument("solidity_file", nargs="*")
commands = parser.add_argument_group("commands")
@ -227,25 +258,8 @@ def main():
)
parser.add_argument("--epic", action="store_true", help=argparse.SUPPRESS)
# Get config values
args = parser.parse_args()
if args.epic:
path = os.path.dirname(os.path.realpath(__file__))
sys.argv.remove("--epic")
os.system(" ".join(sys.argv) + " | python3 " + path + "/epic.py")
sys.exit()
if args.version:
if args.outform == "json":
print(json.dumps({"version_str": VERSION}))
else:
print("Mythril version {}".format(VERSION))
sys.exit()
# Parse cmdline args
def validate_args(parser: argparse.ArgumentParser, args: argparse.Namespace):
if not (
args.search
or args.hash
@ -298,194 +312,224 @@ def main():
"--enable-iprof must be used with one of -g, --graph, -x, --fire-lasers, -j and --statespace-json",
)
# -- commands --
def quick_commands(args: argparse.Namespace):
if args.hash:
print(Mythril.hash_for_function_signature(args.hash))
print(MythrilDisassembler.hash_for_function_signature(args.hash))
sys.exit()
try:
# the mythril object should be our main interface
# infura = None, rpc = None, rpctls = None
# solc_args = None, dynld = None, max_recursion_depth = 12):
mythril = Mythril(
solv=args.solv,
dynld=args.dynld,
onchain_storage_access=(not args.no_onchain_storage_access),
solc_args=args.solc_args,
enable_online_lookup=args.query_signature,
def set_config(args: argparse.Namespace):
config = MythrilConfig()
if args.dynld or not args.no_onchain_storage_access and not (args.rpc or args.i):
config.set_api_from_config_path()
if args.address:
# Establish RPC connection if necessary
config.set_api_rpc(rpc=args.rpc, rpctls=args.rpctls)
elif args.search or args.contract_hash_to_address:
# Open LevelDB if necessary
config.set_api_leveldb(
config.leveldb_dir if not args.leveldb_dir else args.leveldb_dir
)
if (
args.dynld
or not args.no_onchain_storage_access
and not (args.rpc or args.i)
):
mythril.set_api_from_config_path()
if args.address:
# Establish RPC connection if necessary
mythril.set_api_rpc(rpc=args.rpc, rpctls=args.rpctls)
elif args.search or args.contract_hash_to_address:
# Open LevelDB if necessary
mythril.set_api_leveldb(
mythril.leveldb_dir if not args.leveldb_dir else args.leveldb_dir
)
return config
def leveldb_search(config: MythrilConfig, args: argparse.Namespace):
if args.search or args.contract_hash_to_address:
leveldb_searcher = MythrilLevelDB(config.eth_db)
if args.search:
# Database search ops
mythril.search_db(args.search)
sys.exit()
leveldb_searcher.search_db(args.search)
if args.contract_hash_to_address:
else:
# search corresponding address
try:
mythril.contract_hash_to_address(args.contract_hash_to_address)
leveldb_searcher.contract_hash_to_address(args.contract_hash_to_address)
except AddressNotFoundError:
print("Address not found.")
sys.exit()
sys.exit()
if args.truffle:
try:
# not really pythonic atm. needs refactoring
mythril.analyze_truffle_project(args)
except FileNotFoundError:
print(
"Build directory not found. Make sure that you start the analysis from the project root, and that 'truffle compile' has executed successfully."
)
sys.exit()
# Load / compile input contracts
address = None
if args.code:
# Load from bytecode
code = args.code[2:] if args.code.startswith("0x") else args.code
address, _ = mythril.load_from_bytecode(code, args.bin_runtime)
elif args.codefile:
bytecode = "".join([l.strip() for l in args.codefile if len(l.strip()) > 0])
bytecode = bytecode[2:] if bytecode.startswith("0x") else bytecode
address, _ = mythril.load_from_bytecode(bytecode, args.bin_runtime)
elif args.address:
# Get bytecode from a contract address
address, _ = mythril.load_from_address(args.address)
elif args.solidity_file:
# Compile Solidity source file(s)
if args.graph and len(args.solidity_file) > 1:
exit_with_error(
args.outform,
"Cannot generate call graphs from multiple input files. Please do it one at a time.",
)
address, _ = mythril.load_from_solidity(args.solidity_file) # list of files
else:
def get_code(disassembler: MythrilDisassembler, args: argparse.Namespace):
address = None
if args.code:
# Load from bytecode
code = args.code[2:] if args.code.startswith("0x") else args.code
address, _ = disassembler.load_from_bytecode(code, args.bin_runtime)
elif args.codefile:
bytecode = "".join([l.strip() for l in args.codefile if len(l.strip()) > 0])
bytecode = bytecode[2:] if bytecode.startswith("0x") else bytecode
address, _ = disassembler.load_from_bytecode(bytecode, args.bin_runtime)
elif args.address:
# Get bytecode from a contract address
address, _ = disassembler.load_from_address(args.address)
elif args.solidity_file:
# Compile Solidity source file(s)
if args.graph and len(args.solidity_file) > 1:
exit_with_error(
args.outform,
"No input bytecode. Please provide EVM code via -c BYTECODE, -a ADDRESS, or -i SOLIDITY_FILES",
"Cannot generate call graphs from multiple input files. Please do it one at a time.",
)
address, _ = disassembler.load_from_solidity(
args.solidity_file
) # list of files
else:
exit_with_error(
args.outform,
"No input bytecode. Please provide EVM code via -c BYTECODE, -a ADDRESS, or -i SOLIDITY_FILES",
)
return address
# Commands
if args.storage:
if not args.address:
exit_with_error(
args.outform,
"To read storage, provide the address of a deployed contract with the -a option.",
)
def execute_command(
disassembler: MythrilDisassembler,
address: str,
parser: argparse.ArgumentParser,
args: argparse.Namespace,
):
storage = mythril.get_state_variable_from_storage(
address=address,
params=[a.strip() for a in args.storage.strip().split(",")],
if args.storage:
if not args.address:
exit_with_error(
args.outform,
"To read storage, provide the address of a deployed contract with the -a option.",
)
print(storage)
elif args.disassemble:
# or mythril.disassemble(mythril.contracts[0])
storage = disassembler.get_state_variable_from_storage(
address=address, params=[a.strip() for a in args.storage.strip().split(",")]
)
print(storage)
return
analyzer = MythrilAnalyzer(
strategy=args.strategy,
disassembler=disassembler,
address=address,
max_depth=args.max_depth,
execution_timeout=args.execution_timeout,
create_timeout=args.create_timeout,
enable_iprof=args.enable_iprof,
onchain_storage_access=not args.no_onchain_storage_access,
)
if args.disassemble:
# or mythril.disassemble(mythril.contracts[0])
if disassembler.contracts[0].code:
print("Runtime Disassembly: \n" + disassembler.contracts[0].get_easm())
if disassembler.contracts[0].creation_code:
print("Disassembly: \n" + disassembler.contracts[0].get_creation_easm())
elif args.graph or args.fire_lasers:
if not disassembler.contracts:
exit_with_error(
args.outform, "input files do not contain any valid contracts"
)
if mythril.contracts[0].code:
print("Runtime Disassembly: \n" + mythril.contracts[0].get_easm())
if mythril.contracts[0].creation_code:
print("Disassembly: \n" + mythril.contracts[0].get_creation_easm())
if args.graph:
html = analyzer.graph_html(
contract=analyzer.contracts[0],
enable_physics=args.enable_physics,
phrackify=args.phrack,
)
elif args.graph or args.fire_lasers:
if not mythril.contracts:
exit_with_error(
args.outform, "input files do not contain any valid contracts"
)
try:
with open(args.graph, "w") as f:
f.write(html)
except Exception as e:
exit_with_error(args.outform, "Error saving graph: " + str(e))
if args.graph:
html = mythril.graph_html(
strategy=args.strategy,
contract=mythril.contracts[0],
address=address,
enable_physics=args.enable_physics,
phrackify=args.phrack,
max_depth=args.max_depth,
execution_timeout=args.execution_timeout,
create_timeout=args.create_timeout,
enable_iprof=args.enable_iprof,
else:
try:
report = analyzer.fire_lasers(
modules=[m.strip() for m in args.modules.strip().split(",")]
if args.modules
else [],
verbose_report=args.verbose_report,
transaction_count=args.transaction_count,
)
try:
with open(args.graph, "w") as f:
f.write(html)
except Exception as e:
exit_with_error(args.outform, "Error saving graph: " + str(e))
else:
try:
report = mythril.fire_lasers(
strategy=args.strategy,
address=address,
modules=[m.strip() for m in args.modules.strip().split(",")]
if args.modules
else [],
verbose_report=args.verbose_report,
max_depth=args.max_depth,
execution_timeout=args.execution_timeout,
create_timeout=args.create_timeout,
transaction_count=args.transaction_count,
enable_iprof=args.enable_iprof,
)
outputs = {
"json": report.as_json(),
"jsonv2": report.as_swc_standard_format(),
"text": report.as_text(),
"markdown": report.as_markdown(),
}
print(outputs[args.outform])
except ModuleNotFoundError as e:
exit_with_error(
args.outform, "Error loading analyis modules: " + format(e)
)
elif args.statespace_json:
if not mythril.contracts:
outputs = {
"json": report.as_json(),
"jsonv2": report.as_swc_standard_format(),
"text": report.as_text(),
"markdown": report.as_markdown(),
}
print(outputs[args.outform])
except ModuleNotFoundError as e:
exit_with_error(
args.outform, "input files do not contain any valid contracts"
args.outform, "Error loading analyis modules: " + format(e)
)
statespace = mythril.dump_statespace(
strategy=args.strategy,
contract=mythril.contracts[0],
address=address,
max_depth=args.max_depth,
execution_timeout=args.execution_timeout,
create_timeout=args.create_timeout,
enable_iprof=args.enable_iprof,
elif args.statespace_json:
if not analyzer.contracts:
exit_with_error(
args.outform, "input files do not contain any valid contracts"
)
try:
with open(args.statespace_json, "w") as f:
json.dump(statespace, f)
except Exception as e:
exit_with_error(args.outform, "Error saving json: " + str(e))
statespace = analyzer.dump_statespace(contract=analyzer.contracts[0])
try:
with open(args.statespace_json, "w") as f:
json.dump(statespace, f)
except Exception as e:
exit_with_error(args.outform, "Error saving json: " + str(e))
else:
parser.print_help()
def parse_args(parser: argparse.ArgumentParser, args: argparse.Namespace) -> None:
"""
Parses the arguments
:param parser: The parser
:param args: The args
"""
if args.epic:
path = os.path.dirname(os.path.realpath(__file__))
sys.argv.remove("--epic")
os.system(" ".join(sys.argv) + " | python3 " + path + "/epic.py")
sys.exit()
if args.version:
if args.outform == "json":
print(json.dumps({"version_str": VERSION}))
else:
parser.print_help()
print("Mythril version {}".format(VERSION))
sys.exit()
# Parse cmdline args
validate_args(parser, args)
try:
quick_commands(args)
config = set_config(args)
leveldb_search(config, args)
dissasembler = MythrilDisassembler(
eth=config.eth,
solc_version=args.solv,
solc_args=args.solc_args,
enable_online_lookup=args.query_signature,
)
if args.truffle:
try:
dissasembler.analyze_truffle_project(args)
except FileNotFoundError:
print(
"Build directory not found. Make sure that you start the analysis from the project root, and that 'truffle compile' has executed successfully."
)
sys.exit()
address = get_code(dissasembler, args)
execute_command(
disassembler=dissasembler, address=address, parser=parser, args=args
)
except CriticalError as ce:
exit_with_error(args.outform, str(ce))
except Exception:
exit_with_error(args.outform, traceback.format_exc())
if __name__ == "__main__":

@ -62,6 +62,9 @@ OPCODE_GAS = {
"XOR": (3, 3),
"NOT": (3, 3),
"BYTE": (3, 3),
"SHL": (3, 3),
"SHR": (3, 3),
"SAR": (3, 3),
"SHA3": (
30,
30 + 6 * 8,
@ -80,6 +83,7 @@ OPCODE_GAS = {
"GASPRICE": (2, 2),
"EXTCODESIZE": (700, 700),
"EXTCODECOPY": (700, 700 + 3 * 768), # https://ethereum.stackexchange.com/a/47556
"EXTCODEHASH": (400, 400),
"RETURNDATASIZE": (2, 2),
"RETURNDATACOPY": (3, 3),
"BLOCKHASH": (20, 20),
@ -176,6 +180,10 @@ OPCODE_GAS = {
"LOG3": (4 * 375, 4 * 375 + 8 * 32),
"LOG4": (5 * 375, 5 * 375 + 8 * 32),
"CREATE": (32000, 32000),
"CREATE2": (
32000,
32000,
), # TODO: The gas value is dynamic, to be done while implementing create2
"CALL": (700, 700 + 9000 + 25000),
"NATIVE_COST": calculate_native_gas,
"CALLCODE": (700, 700 + 9000 + 25000),

@ -26,6 +26,7 @@ from mythril.laser.smt import (
Bool,
Or,
Not,
LShR,
)
from mythril.laser.smt import symbol_factory
@ -464,6 +465,33 @@ class Instruction:
global_state.mstate.stack.append(0 if s1 == 0 else URem(s0, s1))
return [global_state]
@StateTransition()
def shl_(self, global_state: GlobalState) -> List[GlobalState]:
shift, value = (
util.pop_bitvec(global_state.mstate),
util.pop_bitvec(global_state.mstate),
)
global_state.mstate.stack.append(value << shift)
return [global_state]
@StateTransition()
def shr_(self, global_state: GlobalState) -> List[GlobalState]:
shift, value = (
util.pop_bitvec(global_state.mstate),
util.pop_bitvec(global_state.mstate),
)
global_state.mstate.stack.append(LShR(value, shift))
return [global_state]
@StateTransition()
def sar_(self, global_state: GlobalState) -> List[GlobalState]:
shift, value = (
util.pop_bitvec(global_state.mstate),
util.pop_bitvec(global_state.mstate),
)
global_state.mstate.stack.append(value >> shift)
return [global_state]
@StateTransition()
def smod_(self, global_state: GlobalState) -> List[GlobalState]:
"""
@ -1082,6 +1110,20 @@ class Instruction:
return [global_state]
@StateTransition
def extcodehash_(self, global_state: GlobalState) -> List[GlobalState]:
"""
:param global_state:
:return: List of global states possible, list of size 1 in this case
"""
# TODO: To be implemented
address = global_state.mstate.stack.pop()
global_state.mstate.stack.append(
global_state.new_bitvec("extcodehash_{}".format(str(address)), 256)
)
return [global_state]
@StateTransition()
def extcodecopy_(self, global_state: GlobalState) -> List[GlobalState]:
"""
@ -1671,6 +1713,25 @@ class Instruction:
state.stack.append(0)
return [global_state]
@StateTransition()
def create2_(self, global_state: GlobalState) -> List[GlobalState]:
"""
:param global_state:
:return:
"""
# TODO: implement me
state = global_state.mstate
endowment, memory_start, memory_length, salt = (
state.stack.pop(),
state.stack.pop(),
state.stack.pop(),
state.stack.pop(),
)
# Not supported
state.stack.append(0)
return [global_state]
@StateTransition()
def return_(self, global_state: GlobalState):
"""

@ -29,6 +29,11 @@ class Memory:
"""
return len(self._memory)
def __copy__(self):
copy = Memory()
copy._memory = self._memory[:]
return copy
def extend(self, size):
"""

@ -449,5 +449,6 @@ class TaintRunner:
"MSIZE": (0, 1),
"GAS": (0, 1),
"CREATE": (3, 1),
"CREATE2": (4, 1),
"RETURN": (2, 0),
}

@ -13,6 +13,7 @@ from mythril.laser.smt.bitvec import (
BVAddNoOverflow,
BVMulNoOverflow,
BVSubNoUnderflow,
LShR,
)
from mythril.laser.smt.bitvecfunc import BitVecFunc
from mythril.laser.smt.expression import Expression, simplify

@ -1,7 +1,7 @@
"""This module provides classes for an SMT abstraction of bit vectors."""
from typing import Union, overload, List, cast, Any, Optional, Callable
from operator import lshift, rshift
import z3
from mythril.laser.smt.bool import Bool, And, Or
@ -211,6 +211,38 @@ class BitVec(Expression[z3.BitVecRef]):
# MYPY: fix complaints due to z3 overriding __eq__
return Bool(cast(z3.BoolRef, self.raw != other.raw), annotations=union)
def _handle_shift(self, other: Union[int, "BitVec"], operator: Callable) -> "BitVec":
"""
Handles shift
:param other: The other BitVector
:param operator: The shift operator
:return: the resulting output
"""
if isinstance(other, BitVecFunc):
return operator(other, self)
if not isinstance(other, BitVec):
return BitVec(
operator(self.raw, other), annotations=self.annotations
)
union = self.annotations + other.annotations
return BitVec(operator(self.raw, other.raw), annotations=union)
def __lshift__(self, other: Union[int, "BitVec"]) -> "BitVec":
"""
:param other:
:return:
"""
return self._handle_shift(other, lshift)
def __rshift__(self, other: Union[int, "BitVec"]) -> "BitVec":
"""
:param other:
:return:
"""
return self._handle_shift(other, rshift)
def _comparison_helper(
a: BitVec, b: BitVec, operation: Callable, default_value: bool, inputs_equal: bool
@ -254,6 +286,10 @@ def _arithmetic_helper(a: BitVec, b: BitVec, operation: Callable) -> BitVec:
return BitVec(raw, annotations=union)
def LShR(a: BitVec, b: BitVec):
return _arithmetic_helper(a, b, z3.LShR)
def If(a: Union[Bool, bool], b: Union[BitVec, int], c: Union[BitVec, int]) -> BitVec:
"""Create an if-then-else expression.

@ -207,3 +207,19 @@ class BitVecFunc(BitVec):
return _comparison_helper(
self, other, operator.eq, default_value=True, inputs_equal=False
)
def __lshift__(self, other: Union[int, "BitVec"]) -> "BitVec":
"""
Left shift operation
:param other: The int or BitVec to shift on
:return The resulting left shifted output
"""
return _arithmetic_helper(self, other, operator.lshift)
def __rshift__(self, other: Union[int, "BitVec"]) -> "BitVec":
"""
Right shift operation
:param other: The int or BitVec to shift on
:return The resulting right shifted output:
"""
return _arithmetic_helper(self, other, operator.rshift)

@ -570,6 +570,7 @@ class Mythril(object):
"""
all_issues = []
SolverStatistics().enabled = True
exceptions = []
for contract in contracts or self.contracts:
StartTime() # Reinitialize start time for new contracts
try:
@ -600,7 +601,7 @@ class Mythril(object):
+ traceback.format_exc()
)
issues = retrieve_callback_issues(modules)
exceptions.append(traceback.format_exc())
for issue in issues:
issue.add_code_info(contract)
@ -610,7 +611,7 @@ class Mythril(object):
source_data = Source()
source_data.get_source_from_contracts_list(self.contracts)
# Finally, output the results
report = Report(verbose_report, source_data)
report = Report(verbose_report, source_data, exceptions=exceptions)
for issue in all_issues:
report.append_issue(issue)

@ -0,0 +1,4 @@
from .mythril_disassembler import MythrilDisassembler
from .mythril_analyzer import MythrilAnalyzer
from .mythril_config import MythrilConfig
from .mythril_leveldb import MythrilLevelDB

@ -0,0 +1,173 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import logging
import traceback
from typing import Optional, List
from . import MythrilDisassembler
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.traceexplore import get_serializable_statespace
from mythril.analysis.security import fire_lasers, retrieve_callback_issues
from mythril.analysis.report import Report, Issue
from mythril.ethereum.evmcontract import EVMContract
from mythril.laser.smt import SolverStatistics
from mythril.support.start_time import StartTime
log = logging.getLogger(__name__)
class MythrilAnalyzer:
"""
The Mythril Analyzer class
Responsible for the analysis of the smart contracts
"""
def __init__(
self,
disassembler: MythrilDisassembler,
requires_dynld: bool = False,
onchain_storage_access: bool = True,
strategy: str = "dfs",
address: Optional[str] = None,
max_depth: Optional[int] = None,
execution_timeout: Optional[int] = None,
create_timeout: Optional[int] = None,
enable_iprof: bool = False,
):
"""
:param disassembler: The MythrilDisassembler class
:param requires_dynld: whether dynamic loading should be done or not
:param onchain_storage_access: Whether onchain access should be done or not
"""
self.eth = disassembler.eth
self.contracts = disassembler.contracts or [] # type: List[EVMContract]
self.enable_online_lookup = disassembler.enable_online_lookup
self.dynld = requires_dynld
self.onchain_storage_access = onchain_storage_access
self.strategy = strategy
self.address = address
self.max_depth = max_depth
self.execution_timeout = execution_timeout
self.create_timeout = create_timeout
self.enable_iprof = enable_iprof
def dump_statespace(self, contract: EVMContract = None) -> str:
"""
Returns serializable statespace of the contract
:param contract: The Contract on which the analysis should be done
:return: The serialized state space
"""
sym = SymExecWrapper(
contract or self.contracts[0],
self.address,
self.strategy,
dynloader=DynLoader(
self.eth,
storage_loading=self.onchain_storage_access,
contract_loading=self.dynld,
),
max_depth=self.max_depth,
execution_timeout=self.execution_timeout,
create_timeout=self.create_timeout,
enable_iprof=self.enable_iprof,
)
return get_serializable_statespace(sym)
def graph_html(
self,
contract: EVMContract = None,
enable_physics: bool = False,
phrackify: bool = False,
transaction_count: Optional[int] = None,
) -> str:
"""
:param contract: The Contract on which the analysis should be done
:param enable_physics: If true then enables the graph physics simulation
:param phrackify: If true generates Phrack-style call graph
:param transaction_count: The amount of transactions to be executed
:return: The generated graph in html format
"""
sym = SymExecWrapper(
contract or self.contracts[0],
self.address,
self.strategy,
dynloader=DynLoader(
self.eth,
storage_loading=self.onchain_storage_access,
contract_loading=self.dynld,
),
max_depth=self.max_depth,
execution_timeout=self.execution_timeout,
transaction_count=transaction_count,
create_timeout=self.create_timeout,
enable_iprof=self.enable_iprof,
)
return generate_graph(sym, physics=enable_physics, phrackify=phrackify)
def fire_lasers(
self,
modules: Optional[List[str]] = None,
verbose_report: bool = False,
transaction_count: Optional[int] = None,
) -> Report:
"""
:param modules: The analysis modules which should be executed
:param verbose_report: Gives out the transaction sequence of the vulnerability
:param transaction_count: The amount of transactions to be executed
:return: The Report class which contains the all the issues/vulnerabilities
"""
all_issues = [] # type: List[Issue]
SolverStatistics().enabled = True
exceptions = []
for contract in self.contracts:
StartTime() # Reinitialize start time for new contracts
try:
sym = SymExecWrapper(
contract,
self.address,
self.strategy,
dynloader=DynLoader(
self.eth,
storage_loading=self.onchain_storage_access,
contract_loading=self.dynld,
),
max_depth=self.max_depth,
execution_timeout=self.execution_timeout,
create_timeout=self.create_timeout,
transaction_count=transaction_count,
modules=modules,
compulsory_statespace=False,
enable_iprof=self.enable_iprof,
)
issues = fire_lasers(sym, modules)
except KeyboardInterrupt:
log.critical("Keyboard Interrupt")
issues = retrieve_callback_issues(modules)
except Exception:
log.critical(
"Exception occurred, aborting analysis. Please report this issue to the Mythril GitHub page.\n"
+ traceback.format_exc()
)
issues = retrieve_callback_issues(modules)
exceptions.append(traceback.format_exc())
for issue in issues:
issue.add_code_info(contract)
all_issues += issues
log.info("Solver statistics: \n{}".format(str(SolverStatistics())))
source_data = Source()
source_data.get_source_from_contracts_list(self.contracts)
# Finally, output the results
report = Report(verbose_report, source_data, exceptions=exceptions)
for issue in all_issues:
report.append_issue(issue)
return report

@ -0,0 +1,226 @@
import codecs
import logging
import os
import platform
import re
from pathlib import Path
from shutil import copyfile
from configparser import ConfigParser
from typing import Optional
from mythril.exceptions import CriticalError
from mythril.ethereum.interface.rpc.client import EthJsonRpc
from mythril.ethereum.interface.leveldb.client import EthLevelDB
log = logging.getLogger(__name__)
class MythrilConfig:
"""
The Mythril Analyzer class
Responsible for setup of the mythril environment
"""
def __init__(self):
self.mythril_dir = self._init_mythril_dir()
self.config_path = os.path.join(self.mythril_dir, "config.ini")
self.leveldb_dir = None
self._init_config()
self.eth = None # type: Optional[EthJsonRpc]
self.eth_db = None # type: Optional[EthLevelDB]
@staticmethod
def _init_mythril_dir() -> str:
"""
Initializes the mythril dir and config.ini file
:return: The mythril dir's path
"""
try:
mythril_dir = os.environ["MYTHRIL_DIR"]
except KeyError:
mythril_dir = os.path.join(os.path.expanduser("~"), ".mythril")
if not os.path.exists(mythril_dir):
# Initialize data directory
log.info("Creating mythril data directory")
os.mkdir(mythril_dir)
db_path = str(Path(mythril_dir) / "signatures.db")
if not os.path.exists(db_path):
# if the default mythril dir doesn't contain a signature DB
# initialize it with the default one from the project root
asset_dir = Path(__file__).parent.parent / "support" / "assets"
copyfile(str(asset_dir / "signatures.db"), db_path)
return mythril_dir
def _init_config(self):
"""If no config file exists, create it and add default options.
Defaults:-
- Default LevelDB path is specified based on OS
- dynamic loading is set to infura by default in the file
This function also sets self.leveldb_dir path
"""
leveldb_default_path = self._get_default_leveldb_path()
if not os.path.exists(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)
config.optionxform = str
config.read(self.config_path, "utf-8")
if "defaults" not in config.sections():
self._add_default_options(config)
if not config.has_option("defaults", "leveldb_dir"):
self._add_leveldb_option(config, leveldb_default_path)
if not config.has_option("defaults", "dynamic_loading"):
self._add_dynamic_loading_option(config)
with codecs.open(self.config_path, "w", "utf-8") as fp:
config.write(fp)
leveldb_dir = config.get(
"defaults", "leveldb_dir", fallback=leveldb_default_path
)
self.leveldb_dir = os.path.expanduser(leveldb_dir)
@staticmethod
def _get_default_leveldb_path() -> str:
"""
Returns the LevelDB path
:return: The LevelDB path
"""
system = platform.system().lower()
leveldb_fallback_dir = os.path.expanduser("~")
if system.startswith("darwin"):
leveldb_fallback_dir = os.path.join(
leveldb_fallback_dir, "Library", "Ethereum"
)
elif system.startswith("windows"):
leveldb_fallback_dir = os.path.join(
leveldb_fallback_dir, "AppData", "Roaming", "Ethereum"
)
else:
leveldb_fallback_dir = os.path.join(leveldb_fallback_dir, ".ethereum")
return os.path.join(leveldb_fallback_dir, "geth", "chaindata")
@staticmethod
def _add_default_options(config: ConfigParser) -> None:
"""
Adds defaults option to config.ini
:param config: The config file object
:return: None
"""
config.add_section("defaults")
@staticmethod
def _add_leveldb_option(config: ConfigParser, leveldb_fallback_dir: str) -> None:
"""
Sets a default leveldb path in .mythril/config.ini file
:param config: The config file object
:param leveldb_fallback_dir: The leveldb dir to use by default for searches
:return: None
"""
config.set("defaults", "#Default chaindata locations:", "")
config.set("defaults", "#– Mac: ~/Library/Ethereum/geth/chaindata", "")
config.set("defaults", "#– Linux: ~/.ethereum/geth/chaindata", "")
config.set(
"defaults",
"#– Windows: %USERPROFILE%\\AppData\\Roaming\\Ethereum\\geth\\chaindata",
"",
)
config.set("defaults", "leveldb_dir", leveldb_fallback_dir)
@staticmethod
def _add_dynamic_loading_option(config: ConfigParser) -> None:
"""
Sets the dynamic loading config option in .mythril/config.ini file
:param config: The config file object
:return: None
"""
config.set(
"defaults", "#– To connect to Infura use dynamic_loading: infura", ""
)
config.set(
"defaults",
"#– To connect to Rpc use "
"dynamic_loading: HOST:PORT / ganache / infura-[network_name]",
"",
)
config.set(
"defaults", "#– To connect to local host use dynamic_loading: localhost", ""
)
config.set("defaults", "dynamic_loading", "infura")
def set_api_leveldb(self, leveldb_path: str) -> None:
"""
"""
self.eth_db = EthLevelDB(leveldb_path)
def set_api_rpc_infura(self) -> None:
"""Set the RPC mode to INFURA on Mainnet."""
log.info("Using INFURA Main Net for RPC queries")
self.eth = EthJsonRpc("mainnet.infura.io", 443, True)
def set_api_rpc(self, rpc: str = None, rpctls: bool = False) -> None:
"""
Sets the RPC mode to either of ganache or infura
:param rpc: either of the strings - ganache, infura-mainnet, infura-rinkeby, infura-kovan, infura-ropsten
"""
if rpc == "ganache":
rpcconfig = ("localhost", 8545, False)
else:
m = re.match(r"infura-(.*)", rpc)
if m and m.group(1) in ["mainnet", "rinkeby", "kovan", "ropsten"]:
rpcconfig = (m.group(1) + ".infura.io", 443, True)
else:
try:
host, port = rpc.split(":")
rpcconfig = (host, int(port), rpctls)
except ValueError:
raise CriticalError(
"Invalid RPC argument, use 'ganache', 'infura-[network]' or 'HOST:PORT'"
)
if rpcconfig:
log.info("Using RPC settings: %s" % str(rpcconfig))
self.eth = EthJsonRpc(rpcconfig[0], int(rpcconfig[1]), rpcconfig[2])
else:
raise CriticalError("Invalid RPC settings, check help for details.")
def set_api_rpc_localhost(self) -> None:
"""Set the RPC mode to a local instance."""
log.info("Using default RPC settings: http://localhost:8545")
self.eth = EthJsonRpc("localhost", 8545)
def set_api_from_config_path(self) -> None:
"""Set the RPC mode based on a given config file."""
config = ConfigParser(allow_no_value=False)
# TODO: Remove this after this issue https://github.com/python/mypy/issues/2427 is closed
config.optionxform = str # type:ignore
config.read(self.config_path, "utf-8")
if config.has_option("defaults", "dynamic_loading"):
dynamic_loading = config.get("defaults", "dynamic_loading")
else:
dynamic_loading = "infura"
self._set_rpc(dynamic_loading)
def _set_rpc(self, rpc_type: str) -> None:
"""
Sets rpc based on the type
:param rpc_type: The type of connection: like infura, ganache, localhost
:return:
"""
if rpc_type == "infura":
self.set_api_rpc_infura()
elif rpc_type == "localhost":
self.set_api_rpc_localhost()
else:
self.set_api_rpc(rpc_type)

@ -0,0 +1,303 @@
import logging
import re
import solc
import os
from ethereum import utils
from solc.exceptions import SolcError
from typing import List, Tuple, Optional
from mythril.ethereum import util
from mythril.ethereum.interface.rpc.client import EthJsonRpc
from mythril.exceptions import CriticalError, CompilerError, NoContractFoundError
from mythril.support import signatures
from mythril.support.truffle import analyze_truffle_project
from mythril.ethereum.evmcontract import EVMContract
from mythril.ethereum.interface.rpc.exceptions import ConnectionError
from mythril.solidity.soliditycontract import SolidityContract, get_contracts_from_file
log = logging.getLogger(__name__)
class MythrilDisassembler:
"""
The Mythril Disassembler class
Responsible for generating disassembly of smart contracts
- Compiles solc code from file/onchain
- Can also be used to access onchain storage data
"""
def __init__(
self,
eth: Optional[EthJsonRpc] = None,
solc_version: str = None,
solc_args: str = None,
enable_online_lookup: bool = False,
) -> None:
self.solc_binary = self._init_solc_binary(solc_version)
self.solc_args = solc_args
self.eth = eth
self.enable_online_lookup = enable_online_lookup
self.sigs = signatures.SignatureDB(enable_online_lookup=enable_online_lookup)
self.contracts = [] # type: List[EVMContract]
@staticmethod
def _init_solc_binary(version: str) -> str:
"""
Only proper versions are supported. No nightlies, commits etc (such as available in remix).
:param version: Version of the solc binary required
:return: The solc binary of the corresponding version
"""
if not version:
return os.environ.get("SOLC") or "solc"
# tried converting input to semver, seemed not necessary so just slicing for now
main_version = solc.main.get_solc_version_string()
main_version_number = re.match(r"\d+.\d+.\d+", main_version)
if main_version is None:
raise CriticalError(
"Could not extract solc version from string {}".format(main_version)
)
if version == main_version_number:
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:
log.info("Given version is already installed")
else:
try:
solc.install_solc("v" + version)
solc_binary = util.solc_exists(version)
if not solc_binary:
raise SolcError()
except SolcError:
raise CriticalError(
"There was an error when trying to install the specified solc version"
)
log.info("Setting the compiler to %s", solc_binary)
return solc_binary
def load_from_bytecode(
self, code: str, bin_runtime: bool = False, address: Optional[str] = None
) -> Tuple[str, EVMContract]:
"""
Returns the address and the contract class for the given bytecode
:param code: Bytecode
:param bin_runtime: Whether the code is runtime code or creation code
:param address: address of contract
:return: tuple(address, Contract class)
"""
if address is None:
address = util.get_indexed_address(0)
if bin_runtime:
self.contracts.append(
EVMContract(
code=code,
name="MAIN",
enable_online_lookup=self.enable_online_lookup,
)
)
else:
self.contracts.append(
EVMContract(
creation_code=code,
name="MAIN",
enable_online_lookup=self.enable_online_lookup,
)
)
return address, self.contracts[-1] # return address and contract object
def load_from_address(self, address: str) -> Tuple[str, EVMContract]:
"""
Returns the contract given it's on chain address
:param address: The on chain address of a contract
:return: tuple(address, contract)
"""
if not re.match(r"0x[a-fA-F0-9]{40}", address):
raise CriticalError("Invalid contract address. Expected format is '0x...'.")
try:
code = self.eth.eth_getCode(address)
except FileNotFoundError as e:
raise CriticalError("IPC error: " + str(e))
except ConnectionError:
raise CriticalError(
"Could not connect to RPC server. Make sure that your node is running and that RPC parameters are set correctly."
)
except Exception as e:
raise CriticalError("IPC / RPC error: " + str(e))
if code == "0x" or code == "0x0":
raise CriticalError(
"Received an empty response from eth_getCode. Check the contract address and verify that you are on the correct chain."
)
else:
self.contracts.append(
EVMContract(
code, name=address, enable_online_lookup=self.enable_online_lookup
)
)
return address, self.contracts[-1] # return address and contract object
def load_from_solidity(
self, solidity_files: List[str]
) -> Tuple[str, List[SolidityContract]]:
"""
:param solidity_files: List of solidity_files
:return: tuple of address, contract class list
"""
address = util.get_indexed_address(0)
contracts = []
for file in solidity_files:
if ":" in file:
file, contract_name = file.split(":")
else:
contract_name = None
file = os.path.expanduser(file)
try:
# import signatures from solidity source
self.sigs.import_solidity_file(
file, solc_binary=self.solc_binary, solc_args=self.solc_args
)
if contract_name is not None:
contract = SolidityContract(
input_file=file,
name=contract_name,
solc_args=self.solc_args,
solc_binary=self.solc_binary,
)
self.contracts.append(contract)
contracts.append(contract)
else:
for contract in get_contracts_from_file(
input_file=file,
solc_args=self.solc_args,
solc_binary=self.solc_binary,
):
self.contracts.append(contract)
contracts.append(contract)
except FileNotFoundError:
raise CriticalError("Input file not found: " + file)
except CompilerError as e:
raise CriticalError(e)
except NoContractFoundError:
log.error(
"The file " + file + " does not contain a compilable contract."
)
return address, contracts
def analyze_truffle_project(self, *args, **kwargs) -> None:
"""
:param args:
:param kwargs:
:return:
"""
analyze_truffle_project(
self.sigs, *args, **kwargs
) # just passthru by passing signatures for now
@staticmethod
def hash_for_function_signature(func: str) -> str:
"""
Return function names corresponding signature hash
:param func: function name
:return: Its hash signature
"""
return "0x%s" % utils.sha3(func)[:4].hex()
def get_state_variable_from_storage(
self, address: str, params: Optional[List[str]] = None
) -> str:
"""
Get variables from the storage
:param address: The contract address
:param params: The list of parameters
param types: [position, length] or ["mapping", position, key1, key2, ... ]
or [position, length, array]
:return: The corresponding storage slot and its value
"""
params = params or []
(position, length, mappings) = (0, 1, [])
try:
if params[0] == "mapping":
if len(params) < 3:
raise CriticalError("Invalid number of parameters.")
position = int(params[1])
position_formatted = utils.zpad(utils.int_to_big_endian(position), 32)
for i in range(2, len(params)):
key = bytes(params[i], "utf8")
key_formatted = utils.rzpad(key, 32)
mappings.append(
int.from_bytes(
utils.sha3(key_formatted + position_formatted),
byteorder="big",
)
)
length = len(mappings)
if length == 1:
position = mappings[0]
else:
if len(params) >= 4:
raise CriticalError("Invalid number of parameters.")
if len(params) >= 1:
position = int(params[0])
if len(params) >= 2:
length = int(params[1])
if len(params) == 3 and params[2] == "array":
position_formatted = utils.zpad(
utils.int_to_big_endian(position), 32
)
position = int.from_bytes(
utils.sha3(position_formatted), byteorder="big"
)
except ValueError:
raise CriticalError(
"Invalid storage index. Please provide a numeric value."
)
outtxt = []
try:
if length == 1:
outtxt.append(
"{}: {}".format(
position, self.eth.eth_getStorageAt(address, position)
)
)
else:
if len(mappings) > 0:
for i in range(0, len(mappings)):
position = mappings[i]
outtxt.append(
"{}: {}".format(
hex(position),
self.eth.eth_getStorageAt(address, position),
)
)
else:
for i in range(position, position + length):
outtxt.append(
"{}: {}".format(
hex(i), self.eth.eth_getStorageAt(address, i)
)
)
except FileNotFoundError as e:
raise CriticalError("IPC error: " + str(e))
except ConnectionError:
raise CriticalError(
"Could not connect to RPC server. "
"Make sure that your node is running and that RPC parameters are set correctly."
)
return "\n".join(outtxt)

@ -0,0 +1,49 @@
import re
from mythril.exceptions import CriticalError
class MythrilLevelDB:
"""
Class which does search operations on leveldb
There are two DBs
1) Key value pairs of hashes and it's corresponding address
2) The LevelDB Trie
"""
def __init__(self, leveldb):
"""
:param leveldb: Leveldb path
"""
self.leveldb = leveldb
def search_db(self, search):
"""
Searches the corresponding code
:param search: The code part to be searched
"""
def search_callback(_, address, balance):
"""
:param _:
:param address: The address of the contract with the code in search
:param balance: The balance of the corresponding contract
"""
print("Address: " + address + ", balance: " + str(balance))
try:
self.leveldb.search(search, search_callback)
except SyntaxError:
raise CriticalError("Syntax error in search expression.")
def contract_hash_to_address(self, contract_hash):
"""
Returns address of the corresponding hash by searching the leveldb
:param contract_hash: Hash to be searched
"""
if not re.match(r"0x[a-fA-F0-9]{64}", contract_hash):
raise CriticalError("Invalid address hash. Expected format is '0x...'.")
print(self.leveldb.contract_hash_to_address(contract_hash))

@ -161,11 +161,14 @@ class SolidityContract(EVMContract):
if len(mapping) > 2 and len(mapping[2]) > 0:
idx = int(mapping[2])
lineno = (
self.solidity_files[idx]
.data.encode("utf-8")[0:offset]
.count("\n".encode("utf-8"))
+ 1
)
if idx == -1:
lineno = None
else:
lineno = (
self.solidity_files[idx]
.data.encode("utf-8")[0:offset]
.count("\n".encode("utf-8"))
+ 1
)
prev_item = item
mappings.append(SourceMapping(idx, offset, length, lineno, item))

@ -0,0 +1,94 @@
# This pyethereum opcodes file with added opcodes
from typing import Dict, Tuple
opcodes = {
0x00: ("STOP", 0, 0, 0),
0x01: ("ADD", 2, 1, 3),
0x02: ("MUL", 2, 1, 5),
0x03: ("SUB", 2, 1, 3),
0x04: ("DIV", 2, 1, 5),
0x05: ("SDIV", 2, 1, 5),
0x06: ("MOD", 2, 1, 5),
0x07: ("SMOD", 2, 1, 5),
0x08: ("ADDMOD", 3, 1, 8),
0x09: ("MULMOD", 3, 1, 8),
0x0A: ("EXP", 2, 1, 10),
0x0B: ("SIGNEXTEND", 2, 1, 5),
0x10: ("LT", 2, 1, 3),
0x11: ("GT", 2, 1, 3),
0x12: ("SLT", 2, 1, 3),
0x13: ("SGT", 2, 1, 3),
0x14: ("EQ", 2, 1, 3),
0x15: ("ISZERO", 1, 1, 3),
0x16: ("AND", 2, 1, 3),
0x17: ("OR", 2, 1, 3),
0x18: ("XOR", 2, 1, 3),
0x19: ("NOT", 1, 1, 3),
0x1A: ("BYTE", 2, 1, 3),
0x1B: ("SHL", 2, 1, 3),
0x1C: ("SHR", 2, 1, 3),
0x1D: ("SAR", 2, 1, 3),
0x20: ("SHA3", 2, 1, 30),
0x30: ("ADDRESS", 0, 1, 2),
0x31: ("BALANCE", 1, 1, 20), # now 400
0x32: ("ORIGIN", 0, 1, 2),
0x33: ("CALLER", 0, 1, 2),
0x34: ("CALLVALUE", 0, 1, 2),
0x35: ("CALLDATALOAD", 1, 1, 3),
0x36: ("CALLDATASIZE", 0, 1, 2),
0x37: ("CALLDATACOPY", 3, 0, 3),
0x38: ("CODESIZE", 0, 1, 2),
0x39: ("CODECOPY", 3, 0, 3),
0x3A: ("GASPRICE", 0, 1, 2),
0x3B: ("EXTCODESIZE", 1, 1, 20), # now 700
0x3C: ("EXTCODECOPY", 4, 0, 20), # now 700
0x3D: ("RETURNDATASIZE", 0, 1, 2),
0x3E: ("RETURNDATACOPY", 3, 0, 3),
0x3F: ("EXTCODEHASH", 3, 0, 3),
0x40: ("BLOCKHASH", 1, 1, 20),
0x41: ("COINBASE", 0, 1, 2),
0x42: ("TIMESTAMP", 0, 1, 2),
0x43: ("NUMBER", 0, 1, 2),
0x44: ("DIFFICULTY", 0, 1, 2),
0x45: ("GASLIMIT", 0, 1, 2),
0x50: ("POP", 1, 0, 2),
0x51: ("MLOAD", 1, 1, 3),
0x52: ("MSTORE", 2, 0, 3),
0x53: ("MSTORE8", 2, 0, 3),
0x54: ("SLOAD", 1, 1, 50), # 200 now
0x55: ("SSTORE", 2, 0, 0),
0x56: ("JUMP", 1, 0, 8),
0x57: ("JUMPI", 2, 0, 10),
0x58: ("PC", 0, 1, 2),
0x59: ("MSIZE", 0, 1, 2),
0x5A: ("GAS", 0, 1, 2),
0x5B: ("JUMPDEST", 0, 0, 1),
0xA0: ("LOG0", 2, 0, 375),
0xA1: ("LOG1", 3, 0, 750),
0xA2: ("LOG2", 4, 0, 1125),
0xA3: ("LOG3", 5, 0, 1500),
0xA4: ("LOG4", 6, 0, 1875),
0xF0: ("CREATE", 3, 1, 32000),
0xF1: ("CALL", 7, 1, 40), # 700 now
0xF2: ("CALLCODE", 7, 1, 40), # 700 now
0xF3: ("RETURN", 2, 0, 0),
0xF4: ("DELEGATECALL", 6, 1, 40), # 700 now
0xF5: ("CREATE2", 3, 1, 32000),
0xFA: ("STATICCALL", 6, 1, 40),
0xFD: ("REVERT", 2, 0, 0),
0xFF: ("SUICIDE", 1, 0, 0), # 5000 now
} # type: Dict[int, Tuple[str, int, int, int]]
opcodesMetropolis = {0x3D, 0x3E, 0xFA, 0xFD}
for i in range(1, 33):
opcodes[0x5F + i] = ("PUSH" + str(i), 0, 1, 3)
for i in range(1, 17):
opcodes[0x7F + i] = ("DUP" + str(i), i, i + 1, 3)
opcodes[0x8F + i] = ("SWAP" + str(i), i + 1, i + 1, 3)
reverse_opcodes = {}
for o in opcodes:
vars()[opcodes[o][0]] = opcodes[o]
reverse_opcodes[opcodes[o][0]] = o

@ -5,6 +5,7 @@ import logging
import os
import re
import sys
import warnings
from pathlib import PurePath
from ethereum.utils import sha3
@ -20,12 +21,24 @@ from mythril.solidity.soliditycontract import SourceMapping
log = logging.getLogger(__name__)
def format_Warning(message, category, filename, lineno, line=""):
return "{}: {}\n\n".format(str(filename), str(message))
warnings.formatwarning = format_Warning
def analyze_truffle_project(sigs, args):
"""
:param sigs:
:param args:
"""
warnings.warn(
"The option --truffle is being deprecated, Please use the truffle-security plugin, https://github.com/ConsenSys/truffle-security",
FutureWarning,
)
project_root = os.getcwd()
build_dir = os.path.join(project_root, "build", "contracts")
@ -181,7 +194,10 @@ def get_mappings(source, deployed_source_map):
if len(mapping) > 2 and len(mapping[2]) > 0:
idx = int(mapping[2])
lineno = source.encode("utf-8")[0:offset].count("\n".encode("utf-8")) + 1
if idx == -1:
lineno = None
else:
lineno = source.encode("utf-8")[0:offset].count("\n".encode("utf-8")) + 1
prev_item = item
mappings.append(SourceMapping(idx, offset, length, lineno, item))

@ -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.20.0" # NOQA
VERSION = "v0.20.2" # NOQA

@ -212,7 +212,7 @@ contract WalletLibrary is WalletEvents {
}
// throw unless the contract is not yet initialized.
modifier only_uninitialized { require(m_numOwners > 0); _; }
modifier only_uninitialized { require(m_numOwners == 0); _; }
// constructor - just pass on the owner array to the multiowned and
// the limit to daylimit

@ -34,7 +34,7 @@ contract Rubixi {
//Fee functions for creator
function collectAllFees() public onlyowner {
require(collectedFees == 0);
require(collectedFees > 0);
creator.transfer(collectedFees);
collectedFees = 0;
}
@ -43,14 +43,14 @@ contract Rubixi {
_amt *= 1 ether;
if (_amt > collectedFees) collectAllFees();
require(collectedFees == 0);
require(collectedFees > 0);
creator.transfer(_amt);
collectedFees -= _amt;
}
function collectPercentOfFees(uint _pcent) public onlyowner {
require(collectedFees == 0 || _pcent > 100);
require(collectedFees > 0 && _pcent <= 100);
uint feesToCollect = collectedFees / 100 * _pcent;
creator.transfer(feesToCollect);
@ -63,12 +63,12 @@ contract Rubixi {
}
function changeMultiplier(uint _mult) public onlyowner {
require(_mult > 300 || _mult < 120);
require(_mult <= 300 && _mult >= 120);
pyramidMultiplier = _mult;
}
function changeFeePercentage(uint _fee) public onlyowner {
require(_fee > 10);
require(_fee <= 10);
feePercent = _fee;
}

@ -1,5 +1,5 @@
from mythril.analysis.callgraph import generate_graph
from mythril.analysis.symbolic import SymExecWrapper
from mythril.mythril import MythrilAnalyzer, MythrilDisassembler
from mythril.ethereum import util
from mythril.solidity.soliditycontract import EVMContract
from tests import (
@ -22,16 +22,17 @@ class GraphTest(BaseTestCase):
)
contract = EVMContract(input_file.read_text())
sym = SymExecWrapper(
contract,
address=(util.get_indexed_address(0)),
disassembler = MythrilDisassembler()
disassembler.contracts.append(contract)
analyzer = MythrilAnalyzer(
disassembler=disassembler,
strategy="dfs",
transaction_count=1,
execution_timeout=5,
max_depth=30,
address=(util.get_indexed_address(0)),
)
html = generate_graph(sym)
html = analyzer.graph_html(transaction_count=1)
output_current.write_text(html)
lines_expected = re.findall(

@ -0,0 +1,148 @@
import pytest
from mythril.disassembler.disassembly import Disassembly
from mythril.laser.ethereum.state.environment import Environment
from mythril.laser.ethereum.state.account import Account
from mythril.laser.ethereum.state.machine_state import MachineState
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.laser.ethereum.state.world_state import WorldState
from mythril.laser.ethereum.instructions import Instruction
from mythril.laser.ethereum.transaction.transaction_models import MessageCallTransaction
from mythril.laser.smt import symbol_factory, simplify
def get_state():
active_account = Account("0x0", code=Disassembly("60606040"))
environment = Environment(active_account, None, None, None, None, None)
state = GlobalState(None, environment, None, MachineState(gas_limit=8000000))
state.transaction_stack.append(
(MessageCallTransaction(world_state=WorldState(), gas_limit=8000000), None)
)
return state
BVV = symbol_factory.BitVecVal
BV = symbol_factory.BitVecSym
test_data = (
([BVV(-1, 256), BVV(1, 256)], BVV(-1, 256)),
([BVV(23, 256), BVV(257, 256)], BVV(0, 256)),
([BVV(23, 256), BVV(30, 256)], BVV(23 >> 30, 256)),
([BVV(-10, 256), BVV(10, 256)], BVV(-1, 256)),
([BV("a", 256), BV("b", 256)], BV("a", 256) >> BV("b", 256)),
)
@pytest.mark.parametrize("inputs,output", test_data)
def test_sar(inputs, output):
# Arrange
state = get_state()
state.mstate.stack = inputs
instruction = Instruction("sar", dynamic_loader=None)
# Act
new_state = instruction.evaluate(state)[0]
# Assert
assert simplify(new_state.mstate.stack[-1]) == output
@pytest.mark.parametrize(
# Test cases from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#sar-arithmetic-shift-right
"val1, val2, expected ",
(
(
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x00",
"0x0000000000000000000000000000000000000000000000000000000000000001",
),
(
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x01",
"0x0000000000000000000000000000000000000000000000000000000000000000",
),
(
"0x8000000000000000000000000000000000000000000000000000000000000000",
"0x01",
"0xc000000000000000000000000000000000000000000000000000000000000000",
),
(
"0x8000000000000000000000000000000000000000000000000000000000000000",
"0xff",
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
(
"0x8000000000000000000000000000000000000000000000000000000000000000",
"0x0100",
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
(
"0x8000000000000000000000000000000000000000000000000000000000000000",
"0x0101",
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0x00",
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0x01",
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0xff",
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0x0100",
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
(
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x01",
"0x0000000000000000000000000000000000000000000000000000000000000000",
),
(
"0x4000000000000000000000000000000000000000000000000000000000000000",
"0xfe",
"0x0000000000000000000000000000000000000000000000000000000000000001",
),
(
"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0xf8",
"0x000000000000000000000000000000000000000000000000000000000000007f",
),
(
"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0xfe",
"0x0000000000000000000000000000000000000000000000000000000000000001",
),
(
"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0xff",
"0x0000000000000000000000000000000000000000000000000000000000000000",
),
(
"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0x0100",
"0x0000000000000000000000000000000000000000000000000000000000000000",
),
),
)
def test_concrete_sar(val1, val2, expected):
# Arrange
state = get_state()
state.mstate.stack = [BVV(int(val1, 16), 256), BVV(int(val2, 16), 256)]
expected = BVV(int(expected, 16), 256)
instruction = Instruction("sar", dynamic_loader=None)
# Act
new_state = instruction.evaluate(state)[0]
# Assert
assert simplify(new_state.mstate.stack[-1]) == expected

@ -0,0 +1,123 @@
import pytest
from mythril.disassembler.disassembly import Disassembly
from mythril.laser.ethereum.state.environment import Environment
from mythril.laser.ethereum.state.account import Account
from mythril.laser.ethereum.state.machine_state import MachineState
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.laser.ethereum.state.world_state import WorldState
from mythril.laser.ethereum.instructions import Instruction
from mythril.laser.ethereum.transaction.transaction_models import MessageCallTransaction
from mythril.laser.smt import symbol_factory, simplify
def get_state():
active_account = Account("0x0", code=Disassembly("60606040"))
environment = Environment(active_account, None, None, None, None, None)
state = GlobalState(None, environment, None, MachineState(gas_limit=8000000))
state.transaction_stack.append(
(MessageCallTransaction(world_state=WorldState(), gas_limit=8000000), None)
)
return state
BVV = symbol_factory.BitVecVal
BV = symbol_factory.BitVecSym
test_data = (
([BVV(2, 256), BVV(2, 256)], BVV(8, 256)),
([BVV(23, 256), BVV(257, 256)], BVV(0, 256)),
([BVV(23, 256), BVV(30, 256)], BVV(23 * (1 << 30), 256)),
([BV("a", 256), BVV(270, 256)], 0),
([BV("a", 256), BV("b", 256)], BV("a", 256) << BV("b", 256)),
)
@pytest.mark.parametrize("inputs,output,", test_data)
def test_shl(inputs, output):
# Arrange
state = get_state()
state.mstate.stack = inputs
instruction = Instruction("shl", dynamic_loader=None)
# Act
new_state = instruction.evaluate(state)[0]
# Assert
assert simplify(new_state.mstate.stack[-1]) == output
@pytest.mark.parametrize(
# Testcases from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#shl-shift-left
"val1, val2, expected",
(
(
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x00",
"0x0000000000000000000000000000000000000000000000000000000000000001",
),
(
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x01",
"0x0000000000000000000000000000000000000000000000000000000000000002",
),
(
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0xff",
"0x8000000000000000000000000000000000000000000000000000000000000000",
),
(
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x0100",
"0x0000000000000000000000000000000000000000000000000000000000000000",
),
(
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x0101",
"0x0000000000000000000000000000000000000000000000000000000000000000",
),
(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0x00",
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0x01",
"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
),
(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0xff",
"0x8000000000000000000000000000000000000000000000000000000000000000",
),
(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0x0100",
"0x0000000000000000000000000000000000000000000000000000000000000000",
),
(
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x01",
"0x0000000000000000000000000000000000000000000000000000000000000000",
),
(
"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0x01",
"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
),
),
)
def test_concrete_shl(val1, val2, expected):
# Arrange
state = get_state()
state.mstate.stack = [BVV(int(val1, 16), 256), BVV(int(val2, 16), 256)]
expected = BVV(int(expected, 16), 256)
instruction = Instruction("shl", dynamic_loader=None)
# Act
new_state = instruction.evaluate(state)[0]
# Assert
assert simplify(new_state.mstate.stack[-1]) == expected

@ -0,0 +1,125 @@
import pytest
from mythril.disassembler.disassembly import Disassembly
from mythril.laser.ethereum.state.environment import Environment
from mythril.laser.ethereum.state.account import Account
from mythril.laser.ethereum.state.machine_state import MachineState
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.laser.ethereum.state.world_state import WorldState
from mythril.laser.ethereum.instructions import Instruction
from mythril.laser.ethereum.transaction.transaction_models import MessageCallTransaction
from mythril.laser.smt import symbol_factory, simplify, LShR
def get_state():
active_account = Account("0x0", code=Disassembly("60606040"))
environment = Environment(active_account, None, None, None, None, None)
state = GlobalState(None, environment, None, MachineState(gas_limit=8000000))
state.transaction_stack.append(
(MessageCallTransaction(world_state=WorldState(), gas_limit=8000000), None)
)
return state
BVV = symbol_factory.BitVecVal
BV = symbol_factory.BitVecSym
test_data = (
([BVV(33, 256), BVV(4, 256)], BVV(2, 256)),
([BVV(1 << 100, 256), BVV(257, 256)], BVV(0, 256)),
([BVV(23233, 256), BVV(10, 256)], BVV(23233 // (1 << 10), 256)),
([BV("a", 256), BVV(270, 256)], 0),
(
[BV("a", 256), BV("b", 256)],
LShR(BV("a", 256), BV("b", 256)),
), # Current approximate specs
)
@pytest.mark.parametrize("inputs,output,", test_data)
def test_shr(inputs, output):
# Arrange
state = get_state()
state.mstate.stack = inputs
instruction = Instruction("shr", dynamic_loader=None)
# Act
new_state = instruction.evaluate(state)[0]
# Assert
assert simplify(new_state.mstate.stack[-1]) == output
@pytest.mark.parametrize(
# Cases: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#shr-logical-shift-right
"val1, val2, expected",
(
(
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x00",
"0x0000000000000000000000000000000000000000000000000000000000000001",
),
(
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x01",
"0x0000000000000000000000000000000000000000000000000000000000000000",
),
(
"0x8000000000000000000000000000000000000000000000000000000000000000",
"0x01",
"0x4000000000000000000000000000000000000000000000000000000000000000",
),
(
"0x8000000000000000000000000000000000000000000000000000000000000000",
"0xff",
"0x0000000000000000000000000000000000000000000000000000000000000001",
),
(
"0x8000000000000000000000000000000000000000000000000000000000000000",
"0x0100",
"0x0000000000000000000000000000000000000000000000000000000000000000",
),
(
"0x8000000000000000000000000000000000000000000000000000000000000000",
"0x0101",
"0x0000000000000000000000000000000000000000000000000000000000000000",
),
(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0x00",
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0x01",
"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0xff",
"0x0000000000000000000000000000000000000000000000000000000000000001",
),
(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0x0100",
"0x0000000000000000000000000000000000000000000000000000000000000000",
),
(
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x01",
"0x0000000000000000000000000000000000000000000000000000000000000000",
),
),
)
def test_concrete_shr(val1, val2, expected):
state = get_state()
state.mstate.stack = [BVV(int(val1, 16), 256), BVV(int(val2, 16), 256)]
expected = BVV(int(expected, 16), 256)
instruction = Instruction("shr", dynamic_loader=None)
# Act
new_state = instruction.evaluate(state)[0]
# Assert
assert simplify(new_state.mstate.stack[-1]) == expected

@ -1,4 +1,4 @@
from mythril.mythril import Mythril
from mythril.mythril import MythrilDisassembler
from mythril.laser.ethereum.transaction import execute_contract_creation
from mythril.ethereum import util
import mythril.laser.ethereum.svm as svm
@ -13,7 +13,7 @@ from mythril.analysis.symbolic import SymExecWrapper
def test_create():
contract = SolidityContract(
str(tests.TESTDATA_INPUTS_CONTRACTS / "calls.sol"),
solc_binary=Mythril._init_solc_binary("0.5.0"),
solc_binary=MythrilDisassembler._init_solc_binary("0.5.0"),
)
laser_evm = svm.LaserEVM({})
@ -37,7 +37,7 @@ def test_create():
def test_sym_exec():
contract = SolidityContract(
str(tests.TESTDATA_INPUTS_CONTRACTS / "calls.sol"),
solc_binary=Mythril._init_solc_binary("0.5.0"),
solc_binary=MythrilDisassembler._init_solc_binary("0.5.0"),
)
sym = SymExecWrapper(

@ -0,0 +1,31 @@
from pathlib import Path
from mythril.mythril import MythrilDisassembler, MythrilAnalyzer
from mythril.analysis.report import Issue
from mock import patch
@patch("mythril.analysis.report.Issue.add_code_info", return_value=None)
@patch(
"mythril.mythril.mythril_analyzer.fire_lasers",
return_value=[Issue("", "", "234", "101", "title", "0x02445")],
)
@patch("mythril.mythril.mythril_analyzer.SymExecWrapper", return_value=None)
def test_fire_lasers(mock_sym, mock_fire_lasers, mock_code_info):
disassembler = MythrilDisassembler(eth=None)
disassembler.load_from_solidity(
[
str(
(
Path(__file__).parent.parent / "testdata/input_contracts/origin.sol"
).absolute()
)
]
)
analyzer = MythrilAnalyzer(disassembler, strategy="dfs")
issues = analyzer.fire_lasers(modules=[]).sorted_issues()
mock_sym.assert_called()
mock_fire_lasers.assert_called()
mock_code_info.assert_called()
assert len(issues) == 1
assert issues[0]["swc-id"] == "101"

@ -0,0 +1,58 @@
import pytest
from configparser import ConfigParser
from pathlib import Path
from mythril.mythril import MythrilConfig
from mythril.exceptions import CriticalError
def test_config_path_dynloading():
config = MythrilConfig()
config.config_path = str(
Path(__file__).parent.parent / "testdata/mythril_config_inputs/config.ini"
)
config.set_api_from_config_path()
assert config.eth.host == "mainnet.infura.io"
assert config.eth.port == 443
rpc_types_tests = [
("infura", "mainnet.infura.io", 443, True),
("ganache", "localhost", 8545, True),
("infura-rinkeby", "rinkeby.infura.io", 443, True),
("infura-ropsten", "ropsten.infura.io", 443, True),
("infura-kovan", "kovan.infura.io", 443, True),
("localhost", "localhost", 8545, True),
("localhost:9022", "localhost", 9022, True),
("pinfura", None, None, False),
("infura-finkeby", None, None, False),
]
@pytest.mark.parametrize("rpc_type,host,port,success", rpc_types_tests)
def test_set_rpc(rpc_type, host, port, success):
config = MythrilConfig()
if success:
config._set_rpc(rpc_type)
assert config.eth.host == host
assert config.eth.port == port
else:
with pytest.raises(CriticalError):
config._set_rpc(rpc_type)
def test_leveldb_config_addition():
config = ConfigParser()
config.add_section("defaults")
MythrilConfig._add_leveldb_option(config, "test")
assert config.has_section("defaults")
assert config.get("defaults", "leveldb_dir") == "test"
def test_dynld_config_addition():
config = ConfigParser()
config.add_section("defaults")
MythrilConfig._add_dynamic_loading_option(config)
assert config.has_section("defaults")
assert config.get("defaults", "dynamic_loading") == "infura"

@ -0,0 +1,70 @@
import pytest
from mythril.mythril import MythrilConfig, MythrilDisassembler
from mythril.exceptions import CriticalError
storage_test = [
(
["438767356", "3"],
[
"0x1a270efc: 0x0000000000000000000000000000000000000000000000000000000000000000",
"0x1a270efd: 0x0000000000000000000000000000000000000000000000000000000000000000",
"0x1a270efe: 0x0000000000000000000000000000000000000000000000000000000000000000",
],
),
(
["mapping", "4588934759847", "1", "2"],
[
"0x7e523d5aeb10cdb378b0b1f76138c28063a2cb9ec8ff710f42a0972f4d53cf44: "
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0xba36da34ceec88853a2ebdde88e023c6919b90348f41e8905b422dc9ce22301c: "
"0x0000000000000000000000000000000000000000000000000000000000000000",
],
),
(
["mapping", "4588934759847", "10"],
[
"45998575720532480608987132552042185415362901038635143236141343153058112000553: "
"0x0000000000000000000000000000000000000000000000000000000000000000"
],
),
(
["4588934759847", "1", "array"],
[
"30699902832541380821728647136767910246735388184559883985790189062258823875816: "
"0x0000000000000000000000000000000000000000000000000000000000000000"
],
),
]
@pytest.mark.parametrize("params,ans", storage_test)
def test_get_data_from_storage(params, ans):
config = MythrilConfig()
config.set_api_rpc_infura()
disassembler = MythrilDisassembler(eth=config.eth, solc_version="0.4.23")
outtext = disassembler.get_state_variable_from_storage(
"0x76799f77587738bfeef09452df215b63d2cfb08a", params
).split("\n")
assert outtext == ans
storage_test_incorrect_params = [
(["1", "2", "3", "4"]),
(["mapping", "1"]),
(["a", "b", "c"]),
]
@pytest.mark.parametrize("params", storage_test_incorrect_params)
def test_get_data_from_storage_incorrect_params(params):
config = MythrilConfig()
config.set_api_rpc_infura()
disassembler = MythrilDisassembler(eth=config.eth, solc_version="0.4.23")
with pytest.raises(CriticalError):
disassembler.get_state_variable_from_storage(
"0x76799f77587738bfeef09452df215b63d2cfb08a", params
)
def test_solc_install():
MythrilDisassembler(eth=None, solc_version="0.4.19")

@ -0,0 +1,51 @@
import io
import pytest
from contextlib import redirect_stdout
from mock import patch
from mythril.mythril import MythrilLevelDB, MythrilConfig
from mythril.exceptions import CriticalError
@patch("mythril.ethereum.interface.leveldb.client.EthLevelDB.search")
@patch("mythril.ethereum.interface.leveldb.client.ETH_DB", return_value=None)
@patch("mythril.ethereum.interface.leveldb.client.LevelDBReader", return_value=None)
@patch("mythril.ethereum.interface.leveldb.client.LevelDBWriter", return_value=None)
def test_leveldb_code_search(mock_leveldb, f1, f2, f3):
config = MythrilConfig()
config.set_api_leveldb("some path")
leveldb_search = MythrilLevelDB(leveldb=config.eth_db)
leveldb_search.search_db("code#PUSH#")
mock_leveldb.assert_called()
@patch("mythril.ethereum.interface.leveldb.client.ETH_DB", return_value=None)
@patch("mythril.ethereum.interface.leveldb.client.LevelDBReader", return_value=None)
@patch("mythril.ethereum.interface.leveldb.client.LevelDBWriter", return_value=None)
def test_leveldb_hash_search_incorrect_input(f1, f2, f3):
config = MythrilConfig()
config.set_api_leveldb("some path")
leveldb_search = MythrilLevelDB(leveldb=config.eth_db)
with pytest.raises(CriticalError):
leveldb_search.contract_hash_to_address("0x23")
@patch(
"mythril.ethereum.interface.leveldb.client.EthLevelDB.contract_hash_to_address",
return_value="0xddbb615cb2ffaff7233d8a6f3601621de94795e1",
)
@patch("mythril.ethereum.interface.leveldb.client.ETH_DB", return_value=None)
@patch("mythril.ethereum.interface.leveldb.client.LevelDBReader", return_value=None)
@patch("mythril.ethereum.interface.leveldb.client.LevelDBWriter", return_value=None)
def test_leveldb_hash_search_correct_input(mock_hash_to_address, f1, f2, f3):
config = MythrilConfig()
config.set_api_leveldb("some path")
leveldb_search = MythrilLevelDB(leveldb=config.eth_db)
f = io.StringIO()
with redirect_stdout(f):
leveldb_search.contract_hash_to_address(
"0x0464e651bcc40de28fc7fcde269218d16850bac9689da5f4a6bd640fd3cdf6aa"
)
out = f.getvalue()
mock_hash_to_address.assert_called()
assert out == "0xddbb615cb2ffaff7233d8a6f3601621de94795e1\n"

@ -1,5 +1,5 @@
from mythril.solidity.soliditycontract import SolidityContract
from mythril.mythril import Mythril
from mythril.mythril import MythrilDisassembler
from mythril.laser.ethereum.state.account import Account
from mythril.laser.ethereum.state.machine_state import MachineState
from mythril.laser.ethereum.state.global_state import GlobalState
@ -84,7 +84,8 @@ class NativeTests(BaseTestCase):
def runTest():
""""""
disassembly = SolidityContract(
"./tests/native_tests.sol", solc_binary=Mythril._init_solc_binary("0.5.0")
"./tests/native_tests.sol",
solc_binary=MythrilDisassembler._init_solc_binary("0.5.0"),
).disassembly
account = Account("0x0000000000000000000000000000000000000000", disassembly)
accounts = {account.address: account}

@ -21,6 +21,13 @@ def _fix_debug_data(json_str):
return json.dumps(read_json, sort_keys=True, indent=4)
def _add_jsonv2_stubs(json_str):
read_json = json.loads(json_str)
for issue in read_json[0]["issues"]:
issue["extra"]["discoveryTime"] = "<DISCOVERY-TIME-DATA>"
return json.dumps(read_json, sort_keys=True, indent=4)
def _generate_report(input_file):
contract = EVMContract(input_file.read_text(), enable_online_lookup=False)
sym = SymExecWrapper(
@ -181,7 +188,9 @@ def test_text_report(reports):
def test_jsonv2_report(reports):
_assert_empty_json(
_get_changed_files_json(
lambda report: _fix_path(report.as_swc_standard_format()).strip(),
lambda report: _fix_path(
_add_jsonv2_stubs(report.as_swc_standard_format())
).strip(),
reports,
".jsonv2",
),

@ -1,6 +1,6 @@
from pathlib import Path
from mythril.mythril import Mythril
from mythril.mythril import MythrilDisassembler
from mythril.solidity.soliditycontract import SolidityContract
from tests import BaseTestCase
@ -11,7 +11,7 @@ class SolidityContractTest(BaseTestCase):
def test_get_source_info_without_name_gets_latest_contract_info(self):
input_file = TEST_FILES / "multi_contracts.sol"
contract = SolidityContract(
str(input_file), solc_binary=Mythril._init_solc_binary("0.5.0")
str(input_file), solc_binary=MythrilDisassembler._init_solc_binary("0.5.0")
)
code_info = contract.get_source_info(142)
@ -25,7 +25,7 @@ class SolidityContractTest(BaseTestCase):
contract = SolidityContract(
str(input_file),
name="Transfer1",
solc_binary=Mythril._init_solc_binary("0.5.0"),
solc_binary=MythrilDisassembler._init_solc_binary("0.5.0"),
)
code_info = contract.get_source_info(142)
@ -39,7 +39,7 @@ class SolidityContractTest(BaseTestCase):
contract = SolidityContract(
str(input_file),
name="AssertFail",
solc_binary=Mythril._init_solc_binary("0.5.0"),
solc_binary=MythrilDisassembler._init_solc_binary("0.5.0"),
)
code_info = contract.get_source_info(70, constructor=True)

@ -0,0 +1,2 @@
[defaults]
dynamic_loading = infura

@ -1,150 +1,148 @@
[
{
"issues": [
{
"description": {
"head": "The contract executes an external message call.",
"tail": "An external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully."
},
"extra": {
},
"locations": [
{
"sourceMap": "661:1:0"
}
{
"issues": [
{
"description": {
"head": "The contract executes an external message call.",
"tail": "An external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully."
},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "661:1:0"
}
],
"severity": "Low",
"swcID": "SWC-107",
"swcTitle": "Reentrancy"
},
{
"description": {
"head": "The contract executes an external message call.",
"tail": "An external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully."
},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "779:1:0"
}
],
"severity": "Low",
"swcID": "SWC-107",
"swcTitle": "Reentrancy"
},
{
"description": {
"head": "The contract executes an external message call.",
"tail": "An external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully."
},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "858:1:0"
}
],
"severity": "Low",
"swcID": "SWC-107",
"swcTitle": "Reentrancy"
},
{
"description": {
"head": "A call to a user-supplied address is executed.",
"tail": "The callee address of an external message call can be set by the caller. Note that the callee can contain arbitrary code and may re-enter any function in this contract. Review the business logic carefully to prevent averse effects on the contract state."
},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "912:1:0"
}
],
"severity": "Medium",
"swcID": "SWC-107",
"swcTitle": "Reentrancy"
},
{
"description": {
"head": "The return value of a message call is not checked.",
"tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states."
},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "661:1:0"
}
],
"severity": "Low",
"swcID": "SWC-104",
"swcTitle": "Unchecked Call Return Value"
},
{
"description": {
"head": "The return value of a message call is not checked.",
"tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states."
},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "779:1:0"
}
],
"severity": "Low",
"swcID": "SWC-104",
"swcTitle": "Unchecked Call Return Value"
},
{
"description": {
"head": "The return value of a message call is not checked.",
"tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states."
},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "858:1:0"
}
],
"severity": "Low",
"swcID": "SWC-104",
"swcTitle": "Unchecked Call Return Value"
},
{
"description": {
"head": "The return value of a message call is not checked.",
"tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states."
},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "912:1:0"
}
],
"severity": "Low",
"swcID": "SWC-104",
"swcTitle": "Unchecked Call Return Value"
}
],
"severity": "Low",
"swcID": "SWC-107",
"swcTitle": "Reentrancy"
},
{
"description": {
"head": "The contract executes an external message call.",
"tail": "An external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully."
},
"extra": {
},
"locations": [
{
"sourceMap": "779:1:0"
}
"meta": {},
"sourceFormat": "evm-byzantium-bytecode",
"sourceList": [
"0x7cbb77986c6b1bf6e945cd3fba06d3ea3d28cfc49cdfdc9571ec30703ac5862f"
],
"severity": "Low",
"swcID": "SWC-107",
"swcTitle": "Reentrancy"
},
{
"description": {
"head": "The contract executes an external message call.",
"tail": "An external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully."
},
"extra": {
},
"locations": [
{
"sourceMap": "858:1:0"
}
],
"severity": "Low",
"swcID": "SWC-107",
"swcTitle": "Reentrancy"
},
{
"description": {
"head": "A call to a user-supplied address is executed.",
"tail": "The callee address of an external message call can be set by the caller. Note that the callee can contain arbitrary code and may re-enter any function in this contract. Review the business logic carefully to prevent averse effects on the contract state."
},
"extra": {
},
"locations": [
{
"sourceMap": "912:1:0"
}
],
"severity": "Medium",
"swcID": "SWC-107",
"swcTitle": "Reentrancy"
},
{
"description": {
"head": "The return value of a message call is not checked.",
"tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states."
},
"extra": {
},
"locations": [
{
"sourceMap": "661:1:0"
}
],
"severity": "Low",
"swcID": "SWC-104",
"swcTitle": "Unchecked Call Return Value"
},
{
"description": {
"head": "The return value of a message call is not checked.",
"tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states."
},
"extra": {
},
"locations": [
{
"sourceMap": "779:1:0"
}
],
"severity": "Low",
"swcID": "SWC-104",
"swcTitle": "Unchecked Call Return Value"
},
{
"description": {
"head": "The return value of a message call is not checked.",
"tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states."
},
"extra": {
},
"locations": [
{
"sourceMap": "858:1:0"
}
],
"severity": "Low",
"swcID": "SWC-104",
"swcTitle": "Unchecked Call Return Value"
},
{
"description": {
"head": "The return value of a message call is not checked.",
"tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states."
},
"extra": {
},
"locations": [
{
"sourceMap": "912:1:0"
}
],
"severity": "Low",
"swcID": "SWC-104",
"swcTitle": "Unchecked Call Return Value"
}
],
"meta": {
},
"sourceFormat": "evm-byzantium-bytecode",
"sourceList": [
"0x7cbb77986c6b1bf6e945cd3fba06d3ea3d28cfc49cdfdc9571ec30703ac5862f"
],
"sourceType": "raw-bytecode"
}
"sourceType": "raw-bytecode"
}
]

@ -6,7 +6,9 @@
"head": "A reachable exception has been detected.",
"tail": "It is possible to trigger an exception (opcode 0xfe). Exceptions can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking."
},
"extra": {},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "446:1:0"
@ -21,7 +23,9 @@
"head": "A reachable exception has been detected.",
"tail": "It is possible to trigger an exception (opcode 0xfe). Exceptions can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking."
},
"extra": {},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "484:1:0"
@ -36,7 +40,9 @@
"head": "A reachable exception has been detected.",
"tail": "It is possible to trigger an exception (opcode 0xfe). Exceptions can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking."
},
"extra": {},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "506:1:0"
@ -51,7 +57,9 @@
"head": "A reachable exception has been detected.",
"tail": "It is possible to trigger an exception (opcode 0xfe). Exceptions can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking."
},
"extra": {},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "531:1:0"

@ -33,8 +33,8 @@
"debug": "<DEBUG-DATA>",
"description": "The return value of a message call is not checked.\nExternal calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states.",
"function": "_function_0x9b58bc26",
"max_gas_used": 35922,
"min_gas_used": 1170,
"max_gas_used": 35928,
"min_gas_used": 1176,
"severity": "Low",
"sourceMap": null,
"swc-id": "104",
@ -46,8 +46,8 @@
"debug": "<DEBUG-DATA>",
"description": "A call to a user-supplied address is executed.\nThe callee address of an external message call can be set by the caller. Note that the callee can contain arbitrary code and may re-enter any function in this contract. Review the business logic carefully to prevent averse effects on the contract state.",
"function": "_function_0xeea4c864",
"max_gas_used": 1223,
"min_gas_used": 471,
"max_gas_used": 1229,
"min_gas_used": 477,
"severity": "Medium",
"sourceMap": null,
"swc-id": "107",
@ -59,8 +59,8 @@
"debug": "<DEBUG-DATA>",
"description": "The return value of a message call is not checked.\nExternal calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states.",
"function": "_function_0xeea4c864",
"max_gas_used": 35947,
"min_gas_used": 1195,
"max_gas_used": 35953,
"min_gas_used": 1201,
"severity": "Low",
"sourceMap": null,
"swc-id": "104",

@ -1,67 +1,97 @@
[{
"issues": [{
"description": {
"head": "Use of callcode is deprecated.",
"tail": "The callcode method executes code of another contract in the context of the caller account. Due to a bug in the implementation it does not persist sender and value over the call. It was therefore deprecated and may be removed in the future. Use the delegatecall method instead."
},
"extra": {},
"locations": [{
"sourceMap": "618:1:0"
}],
"severity": "Medium",
"swcID": "SWC-111",
"swcTitle": "Use of Deprecated Solidity Functions"
}, {
"description": {
"head": "A call to a user-supplied address is executed.",
"tail": "The callee address of an external message call can be set by the caller. Note that the callee can contain arbitrary code and may re-enter any function in this contract. Review the business logic carefully to prevent averse effects on the contract state."
},
"extra": {},
"locations": [{
"sourceMap": "1038:1:0"
}],
"severity": "Medium",
"swcID": "SWC-107",
"swcTitle": "Reentrancy"
}, {
"description": {
"head": "The return value of a message call is not checked.",
"tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states."
},
"extra": {},
"locations": [{
"sourceMap": "618:1:0"
}],
"severity": "Low",
"swcID": "SWC-104",
"swcTitle": "Unchecked Call Return Value"
}, {
"description": {
"head": "The return value of a message call is not checked.",
"tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states."
},
"extra": {},
"locations": [{
"sourceMap": "849:1:0"
}],
"severity": "Low",
"swcID": "SWC-104",
"swcTitle": "Unchecked Call Return Value"
}, {
"description": {
"head": "The return value of a message call is not checked.",
"tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states."
},
"extra": {},
"locations": [{
"sourceMap": "1038:1:0"
}],
"severity": "Low",
"swcID": "SWC-104",
"swcTitle": "Unchecked Call Return Value"
}],
"meta": {},
"sourceFormat": "evm-byzantium-bytecode",
"sourceList": ["0x6daec61d05d8f1210661e7e7d1ed6d72bd6ade639398fac1e867aff50abfc1c1"],
"sourceType": "raw-bytecode"
}]
[
{
"issues": [
{
"description": {
"head": "Use of callcode is deprecated.",
"tail": "The callcode method executes code of another contract in the context of the caller account. Due to a bug in the implementation it does not persist sender and value over the call. It was therefore deprecated and may be removed in the future. Use the delegatecall method instead."
},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "618:1:0"
}
],
"severity": "Medium",
"swcID": "SWC-111",
"swcTitle": "Use of Deprecated Solidity Functions"
},
{
"description": {
"head": "A call to a user-supplied address is executed.",
"tail": "The callee address of an external message call can be set by the caller. Note that the callee can contain arbitrary code and may re-enter any function in this contract. Review the business logic carefully to prevent averse effects on the contract state."
},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "1038:1:0"
}
],
"severity": "Medium",
"swcID": "SWC-107",
"swcTitle": "Reentrancy"
},
{
"description": {
"head": "The return value of a message call is not checked.",
"tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states."
},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "618:1:0"
}
],
"severity": "Low",
"swcID": "SWC-104",
"swcTitle": "Unchecked Call Return Value"
},
{
"description": {
"head": "The return value of a message call is not checked.",
"tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states."
},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "849:1:0"
}
],
"severity": "Low",
"swcID": "SWC-104",
"swcTitle": "Unchecked Call Return Value"
},
{
"description": {
"head": "The return value of a message call is not checked.",
"tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states."
},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "1038:1:0"
}
],
"severity": "Low",
"swcID": "SWC-104",
"swcTitle": "Unchecked Call Return Value"
}
],
"meta": {},
"sourceFormat": "evm-byzantium-bytecode",
"sourceList": [
"0x6daec61d05d8f1210661e7e7d1ed6d72bd6ade639398fac1e867aff50abfc1c1"
],
"sourceType": "raw-bytecode"
}
]

@ -32,7 +32,7 @@ The callcode method executes code of another contract in the context of the call
- Contract: Unknown
- Function name: `_function_0x9b58bc26`
- PC address: 849
- Estimated Gas Usage: 1170 - 35922
- Estimated Gas Usage: 1176 - 35928
### Description
@ -45,7 +45,7 @@ External calls return a boolean value. If the callee contract halts with an exce
- Contract: Unknown
- Function name: `_function_0xeea4c864`
- PC address: 1038
- Estimated Gas Usage: 471 - 1223
- Estimated Gas Usage: 477 - 1229
### Description
@ -58,7 +58,7 @@ The callee address of an external message call can be set by the caller. Note th
- Contract: Unknown
- Function name: `_function_0xeea4c864`
- PC address: 1038
- Estimated Gas Usage: 1195 - 35947
- Estimated Gas Usage: 1201 - 35953
### Description

@ -26,7 +26,7 @@ Severity: Low
Contract: Unknown
Function name: _function_0x9b58bc26
PC address: 849
Estimated Gas Usage: 1170 - 35922
Estimated Gas Usage: 1176 - 35928
The return value of a message call is not checked.
External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states.
--------------------
@ -37,7 +37,7 @@ Severity: Medium
Contract: Unknown
Function name: _function_0xeea4c864
PC address: 1038
Estimated Gas Usage: 471 - 1223
Estimated Gas Usage: 477 - 1229
A call to a user-supplied address is executed.
The callee address of an external message call can be set by the caller. Note that the callee can contain arbitrary code and may re-enter any function in this contract. Review the business logic carefully to prevent averse effects on the contract state.
--------------------
@ -48,7 +48,7 @@ Severity: Low
Contract: Unknown
Function name: _function_0xeea4c864
PC address: 1038
Estimated Gas Usage: 1195 - 35947
Estimated Gas Usage: 1201 - 35953
The return value of a message call is not checked.
External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states.
--------------------

@ -6,7 +6,9 @@
"head": "Anyone can withdraw ETH from the contract account.",
"tail": "Arbitrary senders other than the contract creator can withdraw ETH from the contract account without previously having sent an equivalent amount of ETH to it. This is likely to be a vulnerability."
},
"extra": {},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "142:1:0"

@ -6,7 +6,9 @@
"head": "Use of tx.origin is deprecated.",
"tail": "The smart contract retrieves the transaction origin (tx.origin) using msg.origin. Use of msg.origin is deprecated and the instruction may be removed in the future. Use msg.sender instead.\nSee also: https://solidity.readthedocs.io/en/develop/security-considerations.html#tx-origin"
},
"extra": {},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "317:1:0"

@ -7,8 +7,8 @@
"debug": "<DEBUG-DATA>",
"description": "The binary subtraction can underflow.\nThe operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion.",
"function": "sendeth(address,uint256)",
"max_gas_used": 78152,
"min_gas_used": 17016,
"max_gas_used": 78155,
"min_gas_used": 17019,
"severity": "High",
"sourceMap": null,
"swc-id": "101",
@ -20,8 +20,8 @@
"debug": "<DEBUG-DATA>",
"description": "The binary subtraction can underflow.\nThe operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion.",
"function": "sendeth(address,uint256)",
"max_gas_used": 78152,
"min_gas_used": 17016,
"max_gas_used": 78155,
"min_gas_used": 17019,
"severity": "High",
"sourceMap": null,
"swc-id": "101",

@ -6,7 +6,9 @@
"head": "The binary subtraction can underflow.",
"tail": "The operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion."
},
"extra": {},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "567:1:0"
@ -21,7 +23,9 @@
"head": "The binary subtraction can underflow.",
"tail": "The operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion."
},
"extra": {},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "649:1:0"

@ -6,7 +6,7 @@
- Contract: Unknown
- Function name: `sendeth(address,uint256)`
- PC address: 567
- Estimated Gas Usage: 17016 - 78152
- Estimated Gas Usage: 17019 - 78155
### Description
@ -19,7 +19,7 @@ The operands of the subtraction operation are not sufficiently constrained. The
- Contract: Unknown
- Function name: `sendeth(address,uint256)`
- PC address: 649
- Estimated Gas Usage: 17016 - 78152
- Estimated Gas Usage: 17019 - 78155
### Description

@ -4,7 +4,7 @@ Severity: High
Contract: Unknown
Function name: sendeth(address,uint256)
PC address: 567
Estimated Gas Usage: 17016 - 78152
Estimated Gas Usage: 17019 - 78155
The binary subtraction can underflow.
The operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion.
--------------------
@ -15,7 +15,7 @@ Severity: High
Contract: Unknown
Function name: sendeth(address,uint256)
PC address: 649
Estimated Gas Usage: 17016 - 78152
Estimated Gas Usage: 17019 - 78155
The binary subtraction can underflow.
The operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion.
--------------------

@ -6,7 +6,9 @@
"head": "The contract executes an external message call.",
"tail": "An external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully."
},
"extra": {},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "196:1:0"
@ -21,7 +23,9 @@
"head": "The contract executes an external message call.",
"tail": "An external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully."
},
"extra": {},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "285:1:0"
@ -36,7 +40,9 @@
"head": "The return value of a message call is not checked.",
"tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states."
},
"extra": {},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "285:1:0"

@ -1,27 +1,29 @@
[
{
"issues" : [
{
"swcTitle" : "Unprotected SELFDESTRUCT Instruction",
"locations" : [
{
"sourceMap" : "146:1:0"
}
],
"extra" : {},
"description" : {
"tail" : "Anyone can kill this contract and withdraw its balance to an arbitrary address.",
"head" : "The contract can be killed by anyone."
},
"severity" : "High",
"swcID" : "SWC-106"
}
],
"sourceFormat" : "evm-byzantium-bytecode",
"meta" : {},
"sourceType" : "raw-bytecode",
"sourceList" : [
"0x2fb801366b61a05b30550481a1c8f7d5f20de0b93d9f2f2ce2b28c4e322033c9"
]
}
{
"issues": [
{
"description": {
"head": "The contract can be killed by anyone.",
"tail": "Anyone can kill this contract and withdraw its balance to an arbitrary address."
},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "146:1:0"
}
],
"severity": "High",
"swcID": "SWC-106",
"swcTitle": "Unprotected SELFDESTRUCT Instruction"
}
],
"meta": {},
"sourceFormat": "evm-byzantium-bytecode",
"sourceList": [
"0x2fb801366b61a05b30550481a1c8f7d5f20de0b93d9f2f2ce2b28c4e322033c9"
],
"sourceType": "raw-bytecode"
}
]

@ -7,8 +7,8 @@
"debug": "<DEBUG-DATA>",
"description": "The binary subtraction can underflow.\nThe operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion.",
"function": "sendeth(address,uint256)",
"max_gas_used": 52858,
"min_gas_used": 11912,
"max_gas_used": 52861,
"min_gas_used": 11915,
"severity": "High",
"sourceMap": null,
"swc-id": "101",
@ -20,8 +20,8 @@
"debug": "<DEBUG-DATA>",
"description": "The binary subtraction can underflow.\nThe operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion.",
"function": "sendeth(address,uint256)",
"max_gas_used": 52858,
"min_gas_used": 11912,
"max_gas_used": 52861,
"min_gas_used": 11915,
"severity": "High",
"sourceMap": null,
"swc-id": "101",

@ -6,7 +6,9 @@
"head": "The binary subtraction can underflow.",
"tail": "The operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion."
},
"extra": {},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "567:1:0"
@ -21,7 +23,9 @@
"head": "The binary subtraction can underflow.",
"tail": "The operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion."
},
"extra": {},
"extra": {
"discoveryTime": "<DISCOVERY-TIME-DATA>"
},
"locations": [
{
"sourceMap": "649:1:0"

@ -6,7 +6,7 @@
- Contract: Unknown
- Function name: `sendeth(address,uint256)`
- PC address: 567
- Estimated Gas Usage: 11912 - 52858
- Estimated Gas Usage: 11915 - 52861
### Description
@ -19,7 +19,7 @@ The operands of the subtraction operation are not sufficiently constrained. The
- Contract: Unknown
- Function name: `sendeth(address,uint256)`
- PC address: 649
- Estimated Gas Usage: 11912 - 52858
- Estimated Gas Usage: 11915 - 52861
### Description

@ -4,7 +4,7 @@ Severity: High
Contract: Unknown
Function name: sendeth(address,uint256)
PC address: 567
Estimated Gas Usage: 11912 - 52858
Estimated Gas Usage: 11915 - 52861
The binary subtraction can underflow.
The operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion.
--------------------
@ -15,7 +15,7 @@ Severity: High
Contract: Unknown
Function name: sendeth(address,uint256)
PC address: 649
Estimated Gas Usage: 11912 - 52858
Estimated Gas Usage: 11915 - 52861
The binary subtraction can underflow.
The operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion.
--------------------

@ -12,6 +12,7 @@ commands =
mkdir -p {toxinidir}/tests/testdata/outputs_current_laser_result/
py.test -v \
--junitxml={toxworkdir}/output/{envname}/junit.xml \
--disable-pytest-warnings \
{posargs}
[testenv:py36]
@ -35,6 +36,7 @@ commands =
--cov-report=xml:{toxworkdir}/output/{envname}/coverage.xml \
--cov-report=html:{toxworkdir}/output/{envname}/covhtml \
--junitxml={toxworkdir}/output/{envname}/junit.xml \
--disable-pytest-warnings \
{posargs}

Loading…
Cancel
Save