mirror of https://github.com/ConsenSys/mythril
commit
6af60cd6a6
@ -1,45 +1,25 @@ |
||||
from mythril.analysis.callgraph import generate_graph |
||||
""" |
||||
This test only checks whether dumping is successful, not whether the dumped state space makes sense |
||||
""" |
||||
from mythril.mythril import MythrilAnalyzer, MythrilDisassembler |
||||
from mythril.ethereum import util |
||||
from mythril.solidity.soliditycontract import EVMContract |
||||
from tests import ( |
||||
BaseTestCase, |
||||
TESTDATA_INPUTS, |
||||
TESTDATA_OUTPUTS_EXPECTED, |
||||
TESTDATA_OUTPUTS_CURRENT, |
||||
) |
||||
import re |
||||
from tests import TESTDATA_INPUTS |
||||
|
||||
|
||||
class GraphTest(BaseTestCase): |
||||
def test_generate_graph(self): |
||||
for input_file in TESTDATA_INPUTS.iterdir(): |
||||
output_expected = TESTDATA_OUTPUTS_EXPECTED / ( |
||||
input_file.name + ".graph.html" |
||||
) |
||||
output_current = TESTDATA_OUTPUTS_CURRENT / ( |
||||
input_file.name + ".graph.html" |
||||
) |
||||
def test_generate_graph(): |
||||
for input_file in TESTDATA_INPUTS.iterdir(): |
||||
if input_file.name != "origin.sol.o": |
||||
continue |
||||
contract = EVMContract(input_file.read_text()) |
||||
disassembler = MythrilDisassembler() |
||||
disassembler.contracts.append(contract) |
||||
analyzer = MythrilAnalyzer( |
||||
disassembler=disassembler, |
||||
strategy="dfs", |
||||
execution_timeout=5, |
||||
max_depth=30, |
||||
address=(util.get_indexed_address(0)), |
||||
) |
||||
|
||||
contract = EVMContract(input_file.read_text()) |
||||
disassembler = MythrilDisassembler() |
||||
disassembler.contracts.append(contract) |
||||
analyzer = MythrilAnalyzer( |
||||
disassembler=disassembler, |
||||
strategy="dfs", |
||||
execution_timeout=5, |
||||
max_depth=30, |
||||
address=(util.get_indexed_address(0)), |
||||
) |
||||
|
||||
html = analyzer.graph_html(transaction_count=1) |
||||
output_current.write_text(html) |
||||
|
||||
lines_expected = re.findall( |
||||
r"'label': '.*'", str(output_current.read_text()) |
||||
) |
||||
lines_found = re.findall(r"'label': '.*'", str(output_current.read_text())) |
||||
if not (lines_expected == lines_found): |
||||
self.found_changed_files(input_file, output_expected, output_current) |
||||
|
||||
self.assert_and_show_changed_files() |
||||
analyzer.graph_html(transaction_count=1) |
||||
|
@ -1,73 +0,0 @@ |
||||
from mythril.disassembler.disassembly import Disassembly |
||||
from mythril.laser.ethereum.cfg import Node |
||||
from mythril.laser.ethereum.state.environment import Environment |
||||
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.ethereum.state.calldata import ConcreteCalldata |
||||
from mythril.laser.ethereum.svm import LaserEVM |
||||
from mythril.laser.smt import symbol_factory |
||||
|
||||
# contract A { |
||||
# uint256 public val = 10; |
||||
# } |
||||
contract_init_code = "6080604052600a600055348015601457600080fd5b506084806100236000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80633c6bb43614602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea265627a7a72315820d3cfe7a909450a953cbd7e47d8c17477f2609baa5208d043e965efec69d1ed9864736f6c634300050b0032" |
||||
contract_runtime_code = "6080604052348015600f57600080fd5b506004361060285760003560e01c80633c6bb43614602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea265627a7a72315820d3cfe7a909450a953cbd7e47d8c17477f2609baa5208d043e965efec69d1ed9864736f6c634300050b0032" |
||||
|
||||
last_state = None |
||||
created_contract_account = None |
||||
|
||||
|
||||
def execute_create(): |
||||
global last_state |
||||
global created_contract_account |
||||
if not last_state and not created_contract_account: |
||||
code_raw = [] |
||||
for i in range(len(contract_init_code) // 2): |
||||
code_raw.append(int(contract_init_code[2 * i : 2 * (i + 1)], 16)) |
||||
calldata = ConcreteCalldata(0, code_raw) |
||||
|
||||
world_state = WorldState() |
||||
world_state.node = Node("Contract") |
||||
account = world_state.create_account(balance=1000000, address=101) |
||||
account.code = Disassembly("60a760006000f000") |
||||
environment = Environment(account, None, calldata, None, None, None) |
||||
og_state = GlobalState( |
||||
world_state, environment, world_state.node, MachineState(gas_limit=8000000) |
||||
) |
||||
og_state.transaction_stack.append( |
||||
(MessageCallTransaction(world_state=WorldState(), gas_limit=8000000), None) |
||||
) |
||||
|
||||
laser = LaserEVM() |
||||
states = [og_state] |
||||
last_state = og_state |
||||
for state in states: |
||||
new_states, op_code = laser.execute_state(state) |
||||
last_state = state |
||||
if op_code == "STOP": |
||||
break |
||||
states.extend(new_states) |
||||
|
||||
created_contract_address = last_state.mstate.stack[-1].value |
||||
created_contract_account = last_state.world_state.accounts[ |
||||
created_contract_address |
||||
] |
||||
|
||||
return last_state, created_contract_account |
||||
|
||||
|
||||
def test_create_has_code(): |
||||
last_state, created_contract_account = execute_create() |
||||
assert created_contract_account.code.bytecode == contract_runtime_code |
||||
|
||||
|
||||
def test_create_has_storage(): |
||||
last_state, created_contract_account = execute_create() |
||||
storage = created_contract_account.storage |
||||
# From contract, val = 10. |
||||
assert storage[symbol_factory.BitVecVal(0, 256)] == symbol_factory.BitVecVal( |
||||
10, 256 |
||||
) |
@ -0,0 +1,42 @@ |
||||
import pytest |
||||
from mythril.laser.ethereum.natives import blake2b_fcompress |
||||
|
||||
# Test cases taken from https://eips.ethereum.org/EIPS/eip-152. |
||||
# One of the test case is expected to take a few hrs so I ignored it |
||||
@pytest.mark.parametrize( |
||||
"input_hex, expected_result", |
||||
( |
||||
("", ""), |
||||
( |
||||
"00000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", # noqa: E501 |
||||
"", |
||||
), |
||||
( |
||||
"000000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", # noqa: E501 |
||||
"", |
||||
), |
||||
( |
||||
"0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000002", # noqa: E501 |
||||
"", |
||||
), |
||||
( |
||||
"0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", # noqa: E501 |
||||
"08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b", # noqa: E501 |
||||
), |
||||
( |
||||
"0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", # noqa: E501 |
||||
"ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923", # noqa: E501 |
||||
), |
||||
( |
||||
"0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000", # noqa: E501 |
||||
"75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735", # noqa: E501 |
||||
), |
||||
( |
||||
"0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", # noqa: E501 |
||||
"b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421", # noqa: E501 |
||||
), |
||||
), |
||||
) |
||||
def test_blake2(input_hex, expected_result): |
||||
input_hex = bytearray.fromhex(input_hex) |
||||
assert blake2b_fcompress(input_hex) == list(bytearray.fromhex(expected_result)) |
@ -1,93 +0,0 @@ |
||||
from mythril.solidity.soliditycontract import SolidityContract |
||||
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 |
||||
from mythril.laser.ethereum.state.world_state import WorldState |
||||
from mythril.laser.ethereum import svm |
||||
from tests import BaseTestCase |
||||
|
||||
SHA256_TEST = [(0, False) for _ in range(4)] |
||||
RIPEMD160_TEST = [(0, False) for _ in range(2)] |
||||
ECRECOVER_TEST = [(0, False) for _ in range(2)] |
||||
IDENTITY_TEST = [(0, False) for _ in range(2)] |
||||
|
||||
# These are Random numbers to check whether the 'if condition' is entered or not |
||||
# (True means entered) |
||||
SHA256_TEST[0] = (hex(5555555555555555), True) |
||||
SHA256_TEST[1] = (hex(323232325445454546), False) |
||||
SHA256_TEST[2] = (hex(34756834765834658), True) |
||||
SHA256_TEST[3] = (hex(8756476956956795876987), False) |
||||
|
||||
RIPEMD160_TEST[0] = (hex(999999999999999999993), True) |
||||
RIPEMD160_TEST[1] = (hex(1111111111112), False) |
||||
|
||||
ECRECOVER_TEST[0] = (hex(674837568743979857398564869), True) |
||||
ECRECOVER_TEST[1] = (hex(3487683476979311), False) |
||||
|
||||
IDENTITY_TEST[0] = (hex(87426857369875698), True) |
||||
IDENTITY_TEST[1] = (hex(476934798798347), False) |
||||
|
||||
|
||||
def _all_info(laser): |
||||
nodes = {} |
||||
for uid, node in laser.nodes.items(): |
||||
states = [] |
||||
for state in node.states: |
||||
if isinstance(state, MachineState): |
||||
states.append(state.as_dict) |
||||
elif isinstance(state, GlobalState): |
||||
environment = state.environment.as_dict |
||||
environment["active_account"] = environment["active_account"].address |
||||
states.append( |
||||
{ |
||||
"accounts": state.accounts.keys(), |
||||
"environment": environment, |
||||
"mstate": state.mstate.as_dict, |
||||
} |
||||
) |
||||
|
||||
nodes[uid] = { |
||||
"uid": node.uid, |
||||
"contract_name": node.contract_name, |
||||
"start_addr": node.start_addr, |
||||
"states": states, |
||||
"constraints": node.constraints, |
||||
"function_name": node.function_name, |
||||
"flags": str(node.flags), |
||||
} |
||||
|
||||
edges = [edge.as_dict for edge in laser.edges] |
||||
|
||||
return { |
||||
"nodes": nodes, |
||||
"edges": edges, |
||||
"total_states": laser.total_states, |
||||
"max_depth": laser.max_depth, |
||||
} |
||||
|
||||
|
||||
def _test_natives(laser_info, test_list, test_name): |
||||
for i, j in test_list: |
||||
assert (str(i) in laser_info or str(int(i, 16)) in laser_info) == j |
||||
|
||||
|
||||
class NativeTests(BaseTestCase): |
||||
@staticmethod |
||||
def runTest(): |
||||
"""""" |
||||
# The solidity version (0.5.3 at the moment) should be kept in sync with |
||||
# pragma in ./tests/native_tests.sol |
||||
disassembly = SolidityContract("./tests/native_tests.sol").disassembly |
||||
account = Account("0x0000000000000000000000000000000000000000", disassembly) |
||||
world_state = WorldState() |
||||
world_state.put_account(account) |
||||
laser = svm.LaserEVM(max_depth=100, transaction_count=1) |
||||
laser.sym_exec(world_state=world_state, target_address=account.address.value) |
||||
|
||||
laser_info = str(_all_info(laser)) |
||||
|
||||
_test_natives(laser_info, SHA256_TEST, "SHA256") |
||||
_test_natives(laser_info, RIPEMD160_TEST, "RIPEMD160") |
||||
_test_natives(laser_info, ECRECOVER_TEST, "ECRECOVER") |
||||
_test_natives(laser_info, IDENTITY_TEST, "IDENTITY") |
@ -1,112 +0,0 @@ |
||||
pragma solidity ^0.5.0; |
||||
|
||||
|
||||
contract Caller { |
||||
|
||||
//Just some useless variables |
||||
address public fixedAddress; |
||||
address public storedAddress; |
||||
|
||||
//useless (but good for testing as they contribute as decoys) |
||||
uint256 private statevar; |
||||
bytes32 private far; |
||||
|
||||
constructor (address addr) public { |
||||
fixedAddress = addr; |
||||
} |
||||
/* |
||||
// Commented out because this causes laser to enter an infinite loop... :/ |
||||
// It sets the free memory pointer to a symbolic value, and things break |
||||
|
||||
//some typical function as a decoy |
||||
function thisisfine() public { |
||||
(bool success, bytes memory mem) = fixedAddress.call(""); |
||||
} |
||||
*/ |
||||
|
||||
function sha256Test1() public returns (uint256) { |
||||
uint256 i; |
||||
if (sha256(abi.encodePacked("ab", "c")) == sha256("abc")) { |
||||
// True |
||||
i = 5555555555555555; |
||||
} else { |
||||
// False |
||||
i = 323232325445454546; |
||||
} |
||||
return i; |
||||
} |
||||
|
||||
function sha256Test2() public returns (uint256) { |
||||
uint256 i; |
||||
if (sha256("abd") == sha256(abi.encodePacked("ab", "d"))) { |
||||
// True |
||||
i = 34756834765834658; |
||||
} else { |
||||
// False |
||||
i = 8756476956956795876987; |
||||
} |
||||
return i; |
||||
} |
||||
|
||||
function ripemdTest() public returns (uint256) { |
||||
uint256 i; |
||||
bytes20 v1 = ripemd160(""); |
||||
bytes20 v2 = ripemd160("hhhhh"); |
||||
|
||||
if (v1 != v2) { |
||||
// True |
||||
i = 999999999999999999993; |
||||
} else { |
||||
// False |
||||
i = 1111111111112; |
||||
} |
||||
return i; |
||||
} |
||||
|
||||
function ecrecoverTest() public returns (uint256) { |
||||
bytes32 foobar1 = 0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8; |
||||
bytes32 foobar2 = 0x38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e; |
||||
uint8 v = 28; |
||||
bytes32 r = 0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608; |
||||
bytes32 s = 0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada; |
||||
uint256 i; |
||||
address addr1 = ecrecover(keccak256(abi.encodePacked(foobar1)), v, r, s); |
||||
address addr2 = ecrecover(keccak256(abi.encodePacked(foobar1, foobar2)), v, r, s); |
||||
if (addr1 != addr2) { |
||||
// True |
||||
i = 674837568743979857398564869; |
||||
} else { |
||||
// False |
||||
i = 3487683476979311; |
||||
} |
||||
return i; |
||||
} |
||||
|
||||
//identity is invoked here in compiler and not below |
||||
function needIdentityInvoke(uint sea) public returns (uint) { |
||||
return sea; |
||||
} |
||||
|
||||
function identityFunction(int input) public returns(int out) { |
||||
assembly { |
||||
let x := mload(0x40) |
||||
mstore(x, input) |
||||
|
||||
let success := call(500000000, 0x4, 100000, x, 0x20, x, 0x20) |
||||
out := mload(x) |
||||
mstore(0x40, x) |
||||
} |
||||
} |
||||
|
||||
function identityTest1() public returns (uint256) { |
||||
uint256 i; |
||||
if (identityFunction(100) == 100) { |
||||
// True |
||||
i = 87426857369875698; |
||||
} else { |
||||
// False |
||||
i = 476934798798347; |
||||
} |
||||
return i; |
||||
} |
||||
} |
@ -0,0 +1,23 @@ |
||||
from mythril.mythril import MythrilAnalyzer, MythrilDisassembler |
||||
from mythril.ethereum import util |
||||
from mythril.solidity.soliditycontract import EVMContract |
||||
from tests import TESTDATA_INPUTS |
||||
|
||||
|
||||
def test_statespace_dump(): |
||||
for input_file in TESTDATA_INPUTS.iterdir(): |
||||
if input_file.name not in ("origin.sol.o", "suicide.sol.o"): |
||||
# It's too slow, so it's better to skip some tests. |
||||
continue |
||||
contract = EVMContract(input_file.read_text()) |
||||
disassembler = MythrilDisassembler() |
||||
disassembler.contracts.append(contract) |
||||
analyzer = MythrilAnalyzer( |
||||
disassembler=disassembler, |
||||
strategy="dfs", |
||||
execution_timeout=5, |
||||
max_depth=30, |
||||
address=(util.get_indexed_address(0)), |
||||
) |
||||
|
||||
analyzer.dump_statespace(contract=contract) |
Loading…
Reference in new issue