Merge branch 'develop' into bugfix/665

pull/666/head
Bernhard Mueller 6 years ago committed by GitHub
commit c993340372
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 18
      mythril/analysis/solver.py
  2. 11
      mythril/laser/ethereum/instructions.py
  3. 24
      mythril/laser/ethereum/natives.py
  4. 42
      mythril/laser/ethereum/state.py
  5. 6
      mythril/laser/ethereum/transaction/transaction_models.py
  6. 30
      tests/laser/state/calldata_test.py
  7. 25
      tests/laser/transaction/symbolic_test.py

@ -1,4 +1,4 @@
from z3 import Solver, simplify, sat, unknown, FuncInterp, UGE from z3 import Solver, simplify, sat, unknown, FuncInterp, UGE, Optimize
from mythril.exceptions import UnsatError from mythril.exceptions import UnsatError
from mythril.laser.ethereum.transaction.transaction_models import ( from mythril.laser.ethereum.transaction.transaction_models import (
ContractCreationTransaction, ContractCreationTransaction,
@ -6,12 +6,17 @@ from mythril.laser.ethereum.transaction.transaction_models import (
import logging import logging
def get_model(constraints): def get_model(constraints, minimize=(), maximize=()):
s = Solver() s = Optimize()
s.set("timeout", 100000) s.set("timeout", 100000)
for constraint in constraints: for constraint in constraints:
s.add(constraint) s.add(constraint)
for e in minimize:
s.minimize(e)
for e in maximize:
s.maximize(e)
result = s.check() result = s.check()
if result == sat: if result == sat:
return s.model() return s.model()
@ -62,6 +67,7 @@ def get_transaction_sequence(global_state, constraints):
txs = {} txs = {}
creation_tx_ids = [] creation_tx_ids = []
tx_constraints = constraints.copy() tx_constraints = constraints.copy()
minimize = []
for transaction in transaction_sequence: for transaction in transaction_sequence:
tx_id = str(transaction.id) tx_id = str(transaction.id)
@ -74,12 +80,14 @@ def get_transaction_sequence(global_state, constraints):
UGE(max_calldatasize, transaction.call_data.calldatasize) UGE(max_calldatasize, transaction.call_data.calldatasize)
) )
minimize.append(transaction.call_data.calldatasize)
txs[tx_id] = tx_template.copy() txs[tx_id] = tx_template.copy()
else: else:
creation_tx_ids.append(tx_id) creation_tx_ids.append(tx_id)
model = get_model(tx_constraints) model = get_model(tx_constraints, minimize=minimize)
for transaction in transaction_sequence: for transaction in transaction_sequence:
if not isinstance(transaction, ContractCreationTransaction): if not isinstance(transaction, ContractCreationTransaction):
@ -105,7 +113,7 @@ def get_transaction_sequence(global_state, constraints):
if "caller" in name: if "caller" in name:
# caller is 'creator' for creation transactions # caller is 'creator' for creation transactions
tx_id = name.replace("caller", "") tx_id = name.replace("caller", "")
caller = "0x" + ("%x" % model[d].as_long()).zfill(64) caller = "0x" + ("%x" % model[d].as_long()).zfill(40)
txs[tx_id]["caller"] = caller txs[tx_id]["caller"] = caller

@ -417,7 +417,11 @@ class Instruction:
environment = global_state.environment environment = global_state.environment
op0 = state.stack.pop() op0 = state.stack.pop()
state.stack.append(environment.calldata.get_word_at(op0)) value, constraints = environment.calldata.get_word_at(op0)
state.stack.append(value)
state.constraints.extend(constraints)
return [global_state] return [global_state]
@StateTransition() @StateTransition()
@ -497,7 +501,10 @@ class Instruction:
new_memory = [] new_memory = []
for i in range(size): for i in range(size):
new_memory.append(environment.calldata[i_data]) value, constraints = environment.calldata[i_data]
new_memory.append(value)
state.constraints.extend(constraints)
i_data = ( i_data = (
i_data + 1 if isinstance(i_data, int) else simplify(i_data + 1) i_data + 1 if isinstance(i_data, int) else simplify(i_data + 1)
) )

@ -1,6 +1,5 @@
# -*- coding: utf8 -*- # -*- coding: utf8 -*-
import copy
import hashlib import hashlib
import logging import logging
@ -8,7 +7,8 @@ from ethereum.utils import ecrecover_to_pub
from py_ecc.secp256k1 import N as secp256k1n from py_ecc.secp256k1 import N as secp256k1n
from rlp.utils import ALL_BYTES from rlp.utils import ALL_BYTES
from mythril.laser.ethereum.util import bytearray_to_int, sha3 from mythril.laser.ethereum.util import bytearray_to_int, sha3, get_concrete_int
from z3 import Concat, simplify
class NativeContractException(Exception): class NativeContractException(Exception):
@ -70,7 +70,16 @@ def ripemd160(data):
def identity(data): def identity(data):
return copy.copy(data) # Group up into an array of 32 byte words instead
# of an array of bytes. If saved to memory, 32 byte
# words are currently needed, but a correct memory
# implementation would be byte indexed for the most
# part.
return data
result = []
for i in range(0, len(data), 32):
result.append(simplify(Concat(data[i : i + 32])))
return result
def native_contracts(address, data): def native_contracts(address, data):
@ -79,4 +88,11 @@ def native_contracts(address, data):
""" """
functions = (ecrecover, sha256, ripemd160, identity) functions = (ecrecover, sha256, ripemd160, identity)
return functions[address - 1](data.starting_calldata)
try:
data = [get_concrete_int(e) for e in data._calldata]
except TypeError:
# Symbolic calldata
data = data._calldata
return functions[address - 1](data)

@ -4,16 +4,15 @@ from z3 import (
BitVecRef, BitVecRef,
BitVecNumRef, BitVecNumRef,
BitVecSort, BitVecSort,
Solver,
ExprRef, ExprRef,
Concat, Concat,
sat, sat,
simplify, simplify,
Array, Array,
ForAll, ForAll,
Solver,
UGT,
Implies, Implies,
UGE,
UGT,
) )
from z3.z3types import Z3Exception from z3.z3types import Z3Exception
from mythril.disassembler.disassembly import Disassembly from mythril.disassembler.disassembly import Disassembly
@ -45,7 +44,7 @@ class Calldata:
:param starting_calldata: byte array representing the concrete calldata of a transaction :param starting_calldata: byte array representing the concrete calldata of a transaction
""" """
self.tx_id = tx_id self.tx_id = tx_id
if starting_calldata: if not starting_calldata is None:
self._calldata = [] self._calldata = []
self.calldatasize = BitVecVal(len(starting_calldata), 256) self.calldatasize = BitVecVal(len(starting_calldata), 256)
self.concrete = True self.concrete = True
@ -56,32 +55,21 @@ class Calldata:
self.calldatasize = BitVec("{}_calldatasize".format(self.tx_id), 256) self.calldatasize = BitVec("{}_calldatasize".format(self.tx_id), 256)
self.concrete = False self.concrete = False
self.starting_calldata = starting_calldata or []
@property
def constraints(self):
constraints = []
if self.concrete: if self.concrete:
for calldata_byte in self.starting_calldata: for calldata_byte in starting_calldata:
if type(calldata_byte) == int: if type(calldata_byte) == int:
self._calldata.append(BitVecVal(calldata_byte, 8)) self._calldata.append(BitVecVal(calldata_byte, 8))
else: else:
self._calldata.append(calldata_byte) self._calldata.append(calldata_byte)
constraints.append(self.calldatasize == len(self.starting_calldata))
else:
x = BitVec("x", 256)
constraints.append(
ForAll(x, Implies(self[x] != 0, UGT(self.calldatasize, x)))
)
return constraints
def concretized(self, model): def concretized(self, model):
result = [] result = []
for i in range( for i in range(
get_concrete_int(model.eval(self.calldatasize, model_completion=True)) get_concrete_int(model.eval(self.calldatasize, model_completion=True))
): ):
result.append(get_concrete_int(model.eval(self[i], model_completion=True))) result.append(
get_concrete_int(model.eval(self._calldata[i], model_completion=True))
)
return result return result
@ -103,15 +91,23 @@ class Calldata:
except Z3Exception: except Z3Exception:
raise IndexError("Invalid Calldata Slice") raise IndexError("Invalid Calldata Slice")
return simplify(Concat(dataparts)) values, constraints = zip(*dataparts)
result_constraints = []
for c in constraints:
result_constraints.extend(c)
return (simplify(Concat(values)), result_constraints)
if self.concrete: if self.concrete:
try: try:
return self._calldata[get_concrete_int(item)] return (self._calldata[get_concrete_int(item)], ())
except IndexError: except IndexError:
return BitVecVal(0, 8) return (BitVecVal(0, 8), ())
else: else:
return self._calldata[item] constraints = [
Implies(self._calldata[item] != 0, UGT(self.calldatasize, item))
]
return (self._calldata[item], constraints)
class Storage: class Storage:

@ -91,9 +91,6 @@ class MessageCallTransaction:
global_state = GlobalState(self.world_state, environment, None) global_state = GlobalState(self.world_state, environment, None)
global_state.environment.active_function_name = "fallback" global_state.environment.active_function_name = "fallback"
global_state.mstate.constraints.extend(
global_state.environment.calldata.constraints
)
return global_state return global_state
@ -173,9 +170,6 @@ class ContractCreationTransaction:
global_state = GlobalState(self.world_state, environment, None) global_state = GlobalState(self.world_state, environment, None)
global_state.environment.active_function_name = "constructor" global_state.environment.active_function_name = "constructor"
global_state.mstate.constraints.extend(
global_state.environment.calldata.constraints
)
return global_state return global_state

@ -2,7 +2,7 @@ import pytest
from mythril.laser.ethereum.state import Calldata from mythril.laser.ethereum.state import Calldata
from z3 import Solver, simplify from z3 import Solver, simplify
from z3.z3types import Z3Exception from z3.z3types import Z3Exception
from mock import MagicMock
uninitialized_test_data = [ uninitialized_test_data = [
([]), # Empty concrete calldata ([]), # Empty concrete calldata
@ -17,10 +17,12 @@ def test_concrete_calldata_uninitialized_index(starting_calldata):
solver = Solver() solver = Solver()
# Act # Act
value = calldata[100] value, constraint1 = calldata[100]
value2 = calldata.get_word_at(200) value2, constraint2 = calldata.get_word_at(200)
solver.add(constraint1)
solver.add(constraint2)
solver.add(calldata.constraints)
solver.check() solver.check()
model = solver.model() model = solver.model()
@ -38,7 +40,6 @@ def test_concrete_calldata_calldatasize():
solver = Solver() solver = Solver()
# Act # Act
solver.add(calldata.constraints)
solver.check() solver.check()
model = solver.model() model = solver.model()
@ -54,11 +55,11 @@ def test_symbolic_calldata_constrain_index():
solver = Solver() solver = Solver()
# Act # Act
constraint = calldata[100] == 50 value, calldata_constraints = calldata[100]
constraint = value == 50
value = calldata[100] solver.add([constraint] + calldata_constraints)
solver.add(calldata.constraints + [constraint])
solver.check() solver.check()
model = solver.model() model = solver.model()
@ -76,9 +77,10 @@ def test_concrete_calldata_constrain_index():
solver = Solver() solver = Solver()
# Act # Act
constraint = calldata[2] == 3 value, calldata_constraints = calldata[2]
constraint = value == 3
solver.add(calldata.constraints + [constraint]) solver.add([constraint] + calldata_constraints)
result = solver.check() result = solver.check()
# Assert # Assert
@ -88,14 +90,18 @@ def test_concrete_calldata_constrain_index():
def test_concrete_calldata_constrain_index(): def test_concrete_calldata_constrain_index():
# Arrange # Arrange
calldata = Calldata(0) calldata = Calldata(0)
mstate = MagicMock()
mstate.constraints = []
solver = Solver() solver = Solver()
# Act # Act
constraints = [] constraints = []
constraints.append(calldata[51] == 1) value, calldata_constraints = calldata[51]
constraints.append(value == 1)
constraints.append(calldata.calldatasize == 50) constraints.append(calldata.calldatasize == 50)
solver.add(calldata.constraints + constraints) solver.add(constraints + calldata_constraints)
result = solver.check() result = solver.check()
# Assert # Assert

@ -10,9 +10,6 @@ from mythril.laser.ethereum.svm import LaserEVM
from mythril.laser.ethereum.state import WorldState, Account from mythril.laser.ethereum.state import WorldState, Account
import unittest.mock as mock import unittest.mock as mock
from unittest.mock import MagicMock from unittest.mock import MagicMock
from mythril.laser.ethereum.transaction.symbolic import (
_setup_global_state_for_execution,
)
def _is_message_call(_, transaction): def _is_message_call(_, transaction):
@ -70,25 +67,3 @@ def test_execute_contract_creation(mocked_setup: MagicMock):
# laser_evm.exec.assert_called_once() # laser_evm.exec.assert_called_once()
assert laser_evm.exec.call_count == 1 assert laser_evm.exec.call_count == 1
assert len(laser_evm.open_states) == 0 assert len(laser_evm.open_states) == 0
def test_calldata_constraints_in_transaction():
# Arrange
laser_evm = LaserEVM({})
world_state = WorldState()
correct_constraints = [MagicMock(), MagicMock(), MagicMock()]
transaction = MessageCallTransaction(
world_state, Account("ca11ee"), Account("ca114")
)
transaction.call_data = MagicMock()
transaction.call_data.constraints = correct_constraints
# Act
_setup_global_state_for_execution(laser_evm, transaction)
# Assert
state = laser_evm.work_list[0]
for constraint in correct_constraints:
assert constraint in state.environment.calldata.constraints

Loading…
Cancel
Save