mirror of https://github.com/ConsenSys/mythril
commit
6ce4ff490f
@ -0,0 +1,175 @@ |
|||||||
|
from ethereum import opcodes |
||||||
|
from ethereum.utils import ceil32 |
||||||
|
|
||||||
|
|
||||||
|
def calculate_native_gas(size: int, contract: str): |
||||||
|
gas_value = None |
||||||
|
word_num = ceil32(size) // 32 |
||||||
|
if contract == "ecrecover": |
||||||
|
gas_value = opcodes.GECRECOVER |
||||||
|
elif contract == "sha256": |
||||||
|
gas_value = opcodes.GSHA256BASE + word_num * opcodes.GSHA256WORD |
||||||
|
elif contract == "ripemd160": |
||||||
|
gas_value = opcodes.GRIPEMD160BASE + word_num * opcodes.GRIPEMD160WORD |
||||||
|
elif contract == "identity": |
||||||
|
gas_value = opcodes.GIDENTITYBASE + word_num * opcodes.GIDENTITYWORD |
||||||
|
else: |
||||||
|
raise ValueError("Unknown contract type {}".format(contract)) |
||||||
|
return gas_value, gas_value |
||||||
|
|
||||||
|
|
||||||
|
def calculate_sha3_gas(length: int): |
||||||
|
gas_val = 30 + opcodes.GSHA3WORD * (ceil32(length) // 32) |
||||||
|
return gas_val, gas_val |
||||||
|
|
||||||
|
|
||||||
|
# opcode -> (min_gas, max_gas) |
||||||
|
OPCODE_GAS = { |
||||||
|
"STOP": (0, 0), |
||||||
|
"ADD": (3, 3), |
||||||
|
"MUL": (5, 5), |
||||||
|
"SUB": (3, 3), |
||||||
|
"DIV": (5, 5), |
||||||
|
"SDIV": (5, 5), |
||||||
|
"MOD": (5, 5), |
||||||
|
"SMOD": (5, 5), |
||||||
|
"ADDMOD": (8, 8), |
||||||
|
"MULMOD": (8, 8), |
||||||
|
"EXP": (10, 340), # exponent max 2^32 |
||||||
|
"SIGNEXTEND": (5, 5), |
||||||
|
"LT": (3, 3), |
||||||
|
"GT": (3, 3), |
||||||
|
"SLT": (3, 3), |
||||||
|
"SGT": (3, 3), |
||||||
|
"EQ": (3, 3), |
||||||
|
"ISZERO": (3, 3), |
||||||
|
"AND": (3, 3), |
||||||
|
"OR": (3, 3), |
||||||
|
"XOR": (3, 3), |
||||||
|
"NOT": (3, 3), |
||||||
|
"BYTE": (3, 3), |
||||||
|
"SHA3": ( |
||||||
|
30, |
||||||
|
30 + 6 * 8, |
||||||
|
), # max can be larger, but usually storage location with 8 words |
||||||
|
"SHA3_FUNC": calculate_sha3_gas, |
||||||
|
"ADDRESS": (2, 2), |
||||||
|
"BALANCE": (400, 400), |
||||||
|
"ORIGIN": (2, 2), |
||||||
|
"CALLER": (2, 2), |
||||||
|
"CALLVALUE": (2, 2), |
||||||
|
"CALLDATALOAD": (3, 3), |
||||||
|
"CALLDATASIZE": (2, 2), |
||||||
|
"CALLDATACOPY": (2, 2 + 3 * 768), # https://ethereum.stackexchange.com/a/47556 |
||||||
|
"CODESIZE": (2, 2), |
||||||
|
"CODECOPY": (2, 2 + 3 * 768), # https://ethereum.stackexchange.com/a/47556, |
||||||
|
"GASPRICE": (2, 2), |
||||||
|
"EXTCODESIZE": (700, 700), |
||||||
|
"EXTCODECOPY": (700, 700 + 3 * 768), # https://ethereum.stackexchange.com/a/47556 |
||||||
|
"RETURNDATASIZE": (2, 2), |
||||||
|
"RETURNDATACOPY": (3, 3), |
||||||
|
"BLOCKHASH": (20, 20), |
||||||
|
"COINBASE": (2, 2), |
||||||
|
"TIMESTAMP": (2, 2), |
||||||
|
"NUMBER": (2, 2), |
||||||
|
"DIFFICULTY": (2, 2), |
||||||
|
"GASLIMIT": (2, 2), |
||||||
|
"POP": (2, 2), |
||||||
|
# assume 1KB memory r/w cost as upper bound |
||||||
|
"MLOAD": (3, 96), |
||||||
|
"MSTORE": (3, 98), |
||||||
|
"MSTORE8": (3, 98), |
||||||
|
# assume 64 byte r/w cost as upper bound |
||||||
|
"SLOAD": (400, 400), |
||||||
|
"SSTORE": (5000, 25000), |
||||||
|
"JUMP": (8, 8), |
||||||
|
"JUMPI": (10, 10), |
||||||
|
"PC": (2, 2), |
||||||
|
"MSIZE": (2, 2), |
||||||
|
"GAS": (2, 2), |
||||||
|
"JUMPDEST": (1, 1), |
||||||
|
"PUSH1": (3, 3), |
||||||
|
"PUSH2": (3, 3), |
||||||
|
"PUSH3": (3, 3), |
||||||
|
"PUSH4": (3, 3), |
||||||
|
"PUSH5": (3, 3), |
||||||
|
"PUSH6": (3, 3), |
||||||
|
"PUSH7": (3, 3), |
||||||
|
"PUSH8": (3, 3), |
||||||
|
"PUSH9": (3, 3), |
||||||
|
"PUSH10": (3, 3), |
||||||
|
"PUSH11": (3, 3), |
||||||
|
"PUSH12": (3, 3), |
||||||
|
"PUSH13": (3, 3), |
||||||
|
"PUSH14": (3, 3), |
||||||
|
"PUSH15": (3, 3), |
||||||
|
"PUSH16": (3, 3), |
||||||
|
"PUSH17": (3, 3), |
||||||
|
"PUSH18": (3, 3), |
||||||
|
"PUSH19": (3, 3), |
||||||
|
"PUSH20": (3, 3), |
||||||
|
"PUSH21": (3, 3), |
||||||
|
"PUSH22": (3, 3), |
||||||
|
"PUSH23": (3, 3), |
||||||
|
"PUSH24": (3, 3), |
||||||
|
"PUSH25": (3, 3), |
||||||
|
"PUSH26": (3, 3), |
||||||
|
"PUSH27": (3, 3), |
||||||
|
"PUSH28": (3, 3), |
||||||
|
"PUSH29": (3, 3), |
||||||
|
"PUSH30": (3, 3), |
||||||
|
"PUSH31": (3, 3), |
||||||
|
"PUSH32": (3, 3), |
||||||
|
"DUP1": (3, 3), |
||||||
|
"DUP2": (3, 3), |
||||||
|
"DUP3": (3, 3), |
||||||
|
"DUP4": (3, 3), |
||||||
|
"DUP5": (3, 3), |
||||||
|
"DUP6": (3, 3), |
||||||
|
"DUP7": (3, 3), |
||||||
|
"DUP8": (3, 3), |
||||||
|
"DUP9": (3, 3), |
||||||
|
"DUP10": (3, 3), |
||||||
|
"DUP11": (3, 3), |
||||||
|
"DUP12": (3, 3), |
||||||
|
"DUP13": (3, 3), |
||||||
|
"DUP14": (3, 3), |
||||||
|
"DUP15": (3, 3), |
||||||
|
"DUP16": (3, 3), |
||||||
|
"SWAP1": (3, 3), |
||||||
|
"SWAP2": (3, 3), |
||||||
|
"SWAP3": (3, 3), |
||||||
|
"SWAP4": (3, 3), |
||||||
|
"SWAP5": (3, 3), |
||||||
|
"SWAP6": (3, 3), |
||||||
|
"SWAP7": (3, 3), |
||||||
|
"SWAP8": (3, 3), |
||||||
|
"SWAP9": (3, 3), |
||||||
|
"SWAP10": (3, 3), |
||||||
|
"SWAP11": (3, 3), |
||||||
|
"SWAP12": (3, 3), |
||||||
|
"SWAP13": (3, 3), |
||||||
|
"SWAP14": (3, 3), |
||||||
|
"SWAP15": (3, 3), |
||||||
|
"SWAP16": (3, 3), |
||||||
|
# apparently Solidity only allows byte32 as input to the log |
||||||
|
# function. Virtually it could be as large as the block gas limit |
||||||
|
# allows, but let's stick to the reasonable standard here. |
||||||
|
# https://ethereum.stackexchange.com/a/1691 |
||||||
|
"LOG0": (375, 375 + 8 * 32), |
||||||
|
"LOG1": (2 * 375, 2 * 375 + 8 * 32), |
||||||
|
"LOG2": (3 * 375, 3 * 375 + 8 * 32), |
||||||
|
"LOG3": (4 * 375, 4 * 375 + 8 * 32), |
||||||
|
"LOG4": (5 * 375, 5 * 375 + 8 * 32), |
||||||
|
"CREATE": (32000, 32000), |
||||||
|
"CALL": (700, 700 + 9000 + 25000), |
||||||
|
"NATIVE_COST": calculate_native_gas, |
||||||
|
"CALLCODE": (700, 700 + 9000 + 25000), |
||||||
|
"RETURN": (0, 0), |
||||||
|
"DELEGATECALL": (700, 700 + 9000 + 25000), |
||||||
|
"STATICCALL": (700, 700 + 9000 + 25000), |
||||||
|
"REVERT": (0, 0), |
||||||
|
"SUICIDE": (5000, 30000), |
||||||
|
"ASSERT_FAIL": (0, 0), |
||||||
|
"INVALID": (0, 0), |
||||||
|
} |
@ -1,571 +0,0 @@ |
|||||||
import struct |
|
||||||
from z3 import ( |
|
||||||
BitVec, |
|
||||||
BitVecVal, |
|
||||||
BitVecRef, |
|
||||||
BitVecSort, |
|
||||||
ExprRef, |
|
||||||
Concat, |
|
||||||
sat, |
|
||||||
simplify, |
|
||||||
Array, |
|
||||||
ForAll, |
|
||||||
Implies, |
|
||||||
UGE, |
|
||||||
UGT, |
|
||||||
) |
|
||||||
from z3.z3types import Z3Exception |
|
||||||
from mythril.disassembler.disassembly import Disassembly |
|
||||||
from mythril.laser.ethereum.cfg import Node |
|
||||||
from copy import copy, deepcopy |
|
||||||
from enum import Enum |
|
||||||
from random import randint |
|
||||||
from typing import KeysView, Dict, List, Union, Any, Sequence |
|
||||||
from mythril.laser.ethereum.util import get_concrete_int |
|
||||||
|
|
||||||
from mythril.laser.ethereum.evm_exceptions import ( |
|
||||||
StackOverflowException, |
|
||||||
StackUnderflowException, |
|
||||||
) |
|
||||||
|
|
||||||
|
|
||||||
class CalldataType(Enum): |
|
||||||
CONCRETE = 1 |
|
||||||
SYMBOLIC = 2 |
|
||||||
|
|
||||||
|
|
||||||
class Calldata: |
|
||||||
""" |
|
||||||
Calldata class representing the calldata of a transaction |
|
||||||
""" |
|
||||||
|
|
||||||
def __init__(self, tx_id, starting_calldata=None): |
|
||||||
""" |
|
||||||
Constructor for Calldata |
|
||||||
:param tx_id: unique value representing the transaction the calldata is for |
|
||||||
:param starting_calldata: byte array representing the concrete calldata of a transaction |
|
||||||
""" |
|
||||||
self.tx_id = tx_id |
|
||||||
if starting_calldata is not None: |
|
||||||
self._calldata = [] |
|
||||||
self.calldatasize = BitVecVal(len(starting_calldata), 256) |
|
||||||
self.concrete = True |
|
||||||
else: |
|
||||||
self._calldata = Array( |
|
||||||
"{}_calldata".format(self.tx_id), BitVecSort(256), BitVecSort(8) |
|
||||||
) |
|
||||||
self.calldatasize = BitVec("{}_calldatasize".format(self.tx_id), 256) |
|
||||||
self.concrete = False |
|
||||||
|
|
||||||
if self.concrete: |
|
||||||
for calldata_byte in starting_calldata: |
|
||||||
if type(calldata_byte) == int: |
|
||||||
self._calldata.append(BitVecVal(calldata_byte, 8)) |
|
||||||
else: |
|
||||||
self._calldata.append(calldata_byte) |
|
||||||
|
|
||||||
def concretized(self, model): |
|
||||||
result = [] |
|
||||||
for i in range( |
|
||||||
get_concrete_int(model.eval(self.calldatasize, model_completion=True)) |
|
||||||
): |
|
||||||
result.append( |
|
||||||
get_concrete_int(model.eval(self._calldata[i], model_completion=True)) |
|
||||||
) |
|
||||||
|
|
||||||
return result |
|
||||||
|
|
||||||
def get_word_at(self, index: int): |
|
||||||
return self[index : index + 32] |
|
||||||
|
|
||||||
def __getitem__(self, item: Union[int, slice]) -> Any: |
|
||||||
if isinstance(item, slice): |
|
||||||
start, step, stop = item.start, item.step, item.stop |
|
||||||
try: |
|
||||||
if start is None: |
|
||||||
start = 0 |
|
||||||
if step is None: |
|
||||||
step = 1 |
|
||||||
if stop is None: |
|
||||||
stop = self.calldatasize |
|
||||||
current_index = ( |
|
||||||
start if isinstance(start, BitVecRef) else BitVecVal(start, 256) |
|
||||||
) |
|
||||||
dataparts = [] |
|
||||||
while simplify(current_index != stop): |
|
||||||
dataparts.append(self[current_index]) |
|
||||||
current_index = simplify(current_index + step) |
|
||||||
except Z3Exception: |
|
||||||
raise IndexError("Invalid Calldata Slice") |
|
||||||
|
|
||||||
values, constraints = zip(*dataparts) |
|
||||||
result_constraints = [] |
|
||||||
for c in constraints: |
|
||||||
result_constraints.extend(c) |
|
||||||
return simplify(Concat(values)), result_constraints |
|
||||||
|
|
||||||
if self.concrete: |
|
||||||
try: |
|
||||||
return self._calldata[get_concrete_int(item)], () |
|
||||||
except IndexError: |
|
||||||
return BitVecVal(0, 8), () |
|
||||||
else: |
|
||||||
constraints = [ |
|
||||||
Implies(self._calldata[item] != 0, UGT(self.calldatasize, item)) |
|
||||||
] |
|
||||||
|
|
||||||
return self._calldata[item], constraints |
|
||||||
|
|
||||||
|
|
||||||
class Storage: |
|
||||||
""" |
|
||||||
Storage class represents the storage of an Account |
|
||||||
""" |
|
||||||
|
|
||||||
def __init__(self, concrete=False, address=None, dynamic_loader=None): |
|
||||||
""" |
|
||||||
Constructor for Storage |
|
||||||
:param concrete: bool indicating whether to interpret uninitialized storage as concrete versus symbolic |
|
||||||
""" |
|
||||||
self._storage = {} |
|
||||||
self.concrete = concrete |
|
||||||
self.dynld = dynamic_loader |
|
||||||
self.address = address |
|
||||||
|
|
||||||
def __getitem__(self, item: Union[int, slice]) -> Any: |
|
||||||
try: |
|
||||||
return self._storage[item] |
|
||||||
except KeyError: |
|
||||||
if ( |
|
||||||
self.address |
|
||||||
and int(self.address[2:], 16) != 0 |
|
||||||
and (self.dynld and self.dynld.storage_loading) |
|
||||||
): |
|
||||||
try: |
|
||||||
self._storage[item] = int( |
|
||||||
self.dynld.read_storage( |
|
||||||
contract_address=self.address, index=int(item) |
|
||||||
), |
|
||||||
16, |
|
||||||
) |
|
||||||
return self._storage[item] |
|
||||||
except ValueError: |
|
||||||
pass |
|
||||||
if self.concrete: |
|
||||||
return 0 |
|
||||||
self._storage[item] = BitVec("storage_" + str(item), 256) |
|
||||||
return self._storage[item] |
|
||||||
|
|
||||||
def __setitem__(self, key: str, value: ExprRef) -> None: |
|
||||||
self._storage[key] = value |
|
||||||
|
|
||||||
def keys(self) -> KeysView: |
|
||||||
return self._storage.keys() |
|
||||||
|
|
||||||
|
|
||||||
class Account: |
|
||||||
""" |
|
||||||
Account class representing ethereum accounts |
|
||||||
""" |
|
||||||
|
|
||||||
def __init__( |
|
||||||
self, |
|
||||||
address: str, |
|
||||||
code=None, |
|
||||||
contract_name="unknown", |
|
||||||
balance=None, |
|
||||||
concrete_storage=False, |
|
||||||
dynamic_loader=None, |
|
||||||
): |
|
||||||
""" |
|
||||||
Constructor for account |
|
||||||
:param address: Address of the account |
|
||||||
:param code: The contract code of the account |
|
||||||
:param contract_name: The name associated with the account |
|
||||||
:param balance: The balance for the account |
|
||||||
:param concrete_storage: Interpret storage as concrete |
|
||||||
""" |
|
||||||
self.nonce = 0 |
|
||||||
self.code = code or Disassembly("") |
|
||||||
self.balance = balance if balance else BitVec("balance", 256) |
|
||||||
self.storage = Storage( |
|
||||||
concrete_storage, address=address, dynamic_loader=dynamic_loader |
|
||||||
) |
|
||||||
|
|
||||||
# Metadata |
|
||||||
self.address = address |
|
||||||
self.contract_name = contract_name |
|
||||||
|
|
||||||
self.deleted = False |
|
||||||
|
|
||||||
def __str__(self) -> str: |
|
||||||
return str(self.as_dict) |
|
||||||
|
|
||||||
def set_balance(self, balance: ExprRef) -> None: |
|
||||||
self.balance = balance |
|
||||||
|
|
||||||
def add_balance(self, balance: ExprRef) -> None: |
|
||||||
self.balance += balance |
|
||||||
|
|
||||||
@property |
|
||||||
def as_dict(self) -> Dict: |
|
||||||
return { |
|
||||||
"nonce": self.nonce, |
|
||||||
"code": self.code, |
|
||||||
"balance": self.balance, |
|
||||||
"storage": self.storage, |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
class Environment: |
|
||||||
""" |
|
||||||
The environment class represents the current execution environment for the symbolic executor |
|
||||||
""" |
|
||||||
|
|
||||||
def __init__( |
|
||||||
self, |
|
||||||
active_account: Account, |
|
||||||
sender: ExprRef, |
|
||||||
calldata: Calldata, |
|
||||||
gasprice: ExprRef, |
|
||||||
callvalue: ExprRef, |
|
||||||
origin: ExprRef, |
|
||||||
code=None, |
|
||||||
calldata_type=CalldataType.SYMBOLIC, |
|
||||||
): |
|
||||||
# Metadata |
|
||||||
|
|
||||||
self.active_account = active_account |
|
||||||
self.active_function_name = "" |
|
||||||
|
|
||||||
self.address = BitVecVal(int(active_account.address, 16), 256) |
|
||||||
|
|
||||||
# Ib |
|
||||||
self.code = active_account.code if code is None else code |
|
||||||
|
|
||||||
self.sender = sender |
|
||||||
self.calldata = calldata |
|
||||||
self.calldata_type = calldata_type |
|
||||||
self.gasprice = gasprice |
|
||||||
self.origin = origin |
|
||||||
self.callvalue = callvalue |
|
||||||
|
|
||||||
def __str__(self) -> str: |
|
||||||
return str(self.as_dict) |
|
||||||
|
|
||||||
@property |
|
||||||
def as_dict(self) -> Dict: |
|
||||||
return dict( |
|
||||||
active_account=self.active_account, |
|
||||||
sender=self.sender, |
|
||||||
calldata=self.calldata, |
|
||||||
gasprice=self.gasprice, |
|
||||||
callvalue=self.callvalue, |
|
||||||
origin=self.origin, |
|
||||||
calldata_type=self.calldata_type, |
|
||||||
) |
|
||||||
|
|
||||||
|
|
||||||
class Constraints(list): |
|
||||||
""" |
|
||||||
This class should maintain a solver and it's constraints, This class tries to make the Constraints() object |
|
||||||
as a simple list of constraints with some background processing. |
|
||||||
TODO: add the solver to this class after callback refactor |
|
||||||
""" |
|
||||||
|
|
||||||
def __init__(self, constraint_list=None, solver=None, possibility=None): |
|
||||||
super(Constraints, self).__init__(constraint_list or []) |
|
||||||
self.solver = solver |
|
||||||
self.__possibility = possibility |
|
||||||
|
|
||||||
def check_possibility(self): |
|
||||||
return True |
|
||||||
|
|
||||||
def append(self, constraint): |
|
||||||
super(Constraints, self).append(constraint) |
|
||||||
|
|
||||||
def pop(self, index=-1): |
|
||||||
raise NotImplementedError |
|
||||||
|
|
||||||
def __copy__(self): |
|
||||||
constraint_list = super(Constraints, self).copy() |
|
||||||
return Constraints(constraint_list) |
|
||||||
|
|
||||||
def __deepcopy__(self, memodict=None): |
|
||||||
return self.__copy__() |
|
||||||
|
|
||||||
def __add__(self, constraints): |
|
||||||
constraints_list = super(Constraints, self).__add__(constraints) |
|
||||||
return Constraints(constraint_list=constraints_list) |
|
||||||
|
|
||||||
def __iadd__(self, constraints): |
|
||||||
super(Constraints, self).__iadd__(constraints) |
|
||||||
return self |
|
||||||
|
|
||||||
|
|
||||||
class MachineStack(list): |
|
||||||
""" |
|
||||||
Defines EVM stack, overrides the default list to handle overflows |
|
||||||
""" |
|
||||||
|
|
||||||
STACK_LIMIT = 1024 |
|
||||||
|
|
||||||
def __init__(self, default_list=None): |
|
||||||
if default_list is None: |
|
||||||
default_list = [] |
|
||||||
super(MachineStack, self).__init__(default_list) |
|
||||||
|
|
||||||
def append(self, element: BitVec) -> None: |
|
||||||
""" |
|
||||||
:param element: element to be appended to the list |
|
||||||
:function: appends the element to list if the size is less than STACK_LIMIT, else throws an error |
|
||||||
""" |
|
||||||
if super(MachineStack, self).__len__() >= self.STACK_LIMIT: |
|
||||||
raise StackOverflowException( |
|
||||||
"Reached the EVM stack limit of {}, you can't append more " |
|
||||||
"elements".format(self.STACK_LIMIT) |
|
||||||
) |
|
||||||
super(MachineStack, self).append(element) |
|
||||||
|
|
||||||
def pop(self, index=-1) -> BitVec: |
|
||||||
""" |
|
||||||
:param index:index to be popped, same as the list() class. |
|
||||||
:returns popped value |
|
||||||
:function: same as list() class but throws StackUnderflowException for popping from an empty list |
|
||||||
""" |
|
||||||
|
|
||||||
try: |
|
||||||
return super(MachineStack, self).pop(index) |
|
||||||
except IndexError: |
|
||||||
raise StackUnderflowException("Trying to pop from an empty stack") |
|
||||||
|
|
||||||
def __getitem__(self, item: Union[int, slice]) -> Any: |
|
||||||
try: |
|
||||||
return super(MachineStack, self).__getitem__(item) |
|
||||||
except IndexError: |
|
||||||
raise StackUnderflowException( |
|
||||||
"Trying to access a stack element which doesn't exist" |
|
||||||
) |
|
||||||
|
|
||||||
def __add__(self, other): |
|
||||||
""" |
|
||||||
Implement list concatenation if needed |
|
||||||
""" |
|
||||||
raise NotImplementedError("Implement this if needed") |
|
||||||
|
|
||||||
def __iadd__(self, other): |
|
||||||
""" |
|
||||||
Implement list concatenation if needed |
|
||||||
""" |
|
||||||
raise NotImplementedError("Implement this if needed") |
|
||||||
|
|
||||||
|
|
||||||
class MachineState: |
|
||||||
""" |
|
||||||
MachineState represents current machine state also referenced to as \mu |
|
||||||
""" |
|
||||||
|
|
||||||
def __init__( |
|
||||||
self, gas: int, pc=0, stack=None, memory=None, constraints=None, depth=0 |
|
||||||
): |
|
||||||
""" Constructor for machineState """ |
|
||||||
self.pc = pc |
|
||||||
self.stack = MachineStack(stack) |
|
||||||
self.memory = memory or [] |
|
||||||
self.gas = gas |
|
||||||
self.constraints = constraints or Constraints() |
|
||||||
self.depth = depth |
|
||||||
|
|
||||||
def mem_extend(self, start: int, size: int) -> None: |
|
||||||
""" |
|
||||||
Extends the memory of this machine state |
|
||||||
:param start: Start of memory extension |
|
||||||
:param size: Size of memory extension |
|
||||||
""" |
|
||||||
if self.memory_size > start + size: |
|
||||||
return |
|
||||||
m_extend = start + size - self.memory_size |
|
||||||
self.memory.extend(bytearray(m_extend)) |
|
||||||
|
|
||||||
def memory_write(self, offset: int, data: List[int]) -> None: |
|
||||||
""" Writes data to memory starting at offset """ |
|
||||||
self.mem_extend(offset, len(data)) |
|
||||||
self.memory[offset : offset + len(data)] = data |
|
||||||
|
|
||||||
def pop(self, amount=1) -> Union[BitVec, List[BitVec]]: |
|
||||||
""" Pops amount elements from the stack""" |
|
||||||
if amount > len(self.stack): |
|
||||||
raise StackUnderflowException |
|
||||||
values = self.stack[-amount:][::-1] |
|
||||||
del self.stack[-amount:] |
|
||||||
|
|
||||||
return values[0] if amount == 1 else values |
|
||||||
|
|
||||||
def __deepcopy__(self, memodict=None): |
|
||||||
memodict = {} if memodict is None else memodict |
|
||||||
return MachineState( |
|
||||||
gas=self.gas, |
|
||||||
pc=self.pc, |
|
||||||
stack=copy(self.stack), |
|
||||||
memory=copy(self.memory), |
|
||||||
constraints=copy(self.constraints), |
|
||||||
depth=self.depth, |
|
||||||
) |
|
||||||
|
|
||||||
def __str__(self): |
|
||||||
return str(self.as_dict) |
|
||||||
|
|
||||||
@property |
|
||||||
def memory_size(self) -> int: |
|
||||||
return len(self.memory) |
|
||||||
|
|
||||||
@property |
|
||||||
def as_dict(self) -> Dict: |
|
||||||
return dict( |
|
||||||
pc=self.pc, |
|
||||||
stack=self.stack, |
|
||||||
memory=self.memory, |
|
||||||
memsize=self.memory_size, |
|
||||||
gas=self.gas, |
|
||||||
) |
|
||||||
|
|
||||||
|
|
||||||
class GlobalState: |
|
||||||
""" |
|
||||||
GlobalState represents the current globalstate |
|
||||||
""" |
|
||||||
|
|
||||||
def __init__( |
|
||||||
self, |
|
||||||
world_state: "WorldState", |
|
||||||
environment: Environment, |
|
||||||
node: Node, |
|
||||||
machine_state=None, |
|
||||||
transaction_stack=None, |
|
||||||
last_return_data=None, |
|
||||||
): |
|
||||||
""" Constructor for GlobalState""" |
|
||||||
self.node = node |
|
||||||
self.world_state = world_state |
|
||||||
self.environment = environment |
|
||||||
self.mstate = machine_state if machine_state else MachineState(gas=10000000) |
|
||||||
self.transaction_stack = transaction_stack if transaction_stack else [] |
|
||||||
self.op_code = "" |
|
||||||
self.last_return_data = last_return_data |
|
||||||
|
|
||||||
def __copy__(self) -> "GlobalState": |
|
||||||
world_state = copy(self.world_state) |
|
||||||
environment = copy(self.environment) |
|
||||||
mstate = deepcopy(self.mstate) |
|
||||||
transaction_stack = copy(self.transaction_stack) |
|
||||||
return GlobalState( |
|
||||||
world_state, |
|
||||||
environment, |
|
||||||
self.node, |
|
||||||
mstate, |
|
||||||
transaction_stack=transaction_stack, |
|
||||||
last_return_data=self.last_return_data, |
|
||||||
) |
|
||||||
|
|
||||||
@property |
|
||||||
def accounts(self) -> Dict: |
|
||||||
return self.world_state.accounts |
|
||||||
|
|
||||||
# TODO: remove this, as two instructions are confusing |
|
||||||
def get_current_instruction(self) -> Dict: |
|
||||||
""" Gets the current instruction for this GlobalState""" |
|
||||||
|
|
||||||
instructions = self.environment.code.instruction_list |
|
||||||
return instructions[self.mstate.pc] |
|
||||||
|
|
||||||
@property |
|
||||||
def current_transaction( |
|
||||||
self |
|
||||||
) -> Union["MessageCallTransaction", "ContractCreationTransaction", None]: |
|
||||||
# TODO: Remove circular to transaction package to import Transaction classes |
|
||||||
try: |
|
||||||
return self.transaction_stack[-1][0] |
|
||||||
except IndexError: |
|
||||||
return None |
|
||||||
|
|
||||||
@property |
|
||||||
def instruction(self) -> Dict: |
|
||||||
return self.get_current_instruction() |
|
||||||
|
|
||||||
def new_bitvec(self, name: str, size=256) -> BitVec: |
|
||||||
transaction_id = self.current_transaction.id |
|
||||||
return BitVec("{}_{}".format(transaction_id, name), size) |
|
||||||
|
|
||||||
|
|
||||||
class WorldState: |
|
||||||
""" |
|
||||||
The WorldState class represents the world state as described in the yellow paper |
|
||||||
""" |
|
||||||
|
|
||||||
def __init__(self, transaction_sequence=None): |
|
||||||
""" |
|
||||||
Constructor for the world state. Initializes the accounts record |
|
||||||
""" |
|
||||||
self.accounts = {} |
|
||||||
self.node = None |
|
||||||
self.transaction_sequence = transaction_sequence or [] |
|
||||||
|
|
||||||
def __getitem__(self, item: str) -> Account: |
|
||||||
""" |
|
||||||
Gets an account from the worldstate using item as key |
|
||||||
:param item: Address of the account to get |
|
||||||
:return: Account associated with the address |
|
||||||
""" |
|
||||||
return self.accounts[item] |
|
||||||
|
|
||||||
def __copy__(self) -> "WorldState": |
|
||||||
new_world_state = WorldState(transaction_sequence=self.transaction_sequence[:]) |
|
||||||
new_world_state.accounts = copy(self.accounts) |
|
||||||
new_world_state.node = self.node |
|
||||||
return new_world_state |
|
||||||
|
|
||||||
def create_account( |
|
||||||
self, balance=0, address=None, concrete_storage=False, dynamic_loader=None |
|
||||||
) -> Account: |
|
||||||
""" |
|
||||||
Create non-contract account |
|
||||||
:param address: The account's address |
|
||||||
:param balance: Initial balance for the account |
|
||||||
:param concrete_storage: Interpret account storage as concrete |
|
||||||
:param dynamic_loader: used for dynamically loading storage from the block chain |
|
||||||
:return: The new account |
|
||||||
""" |
|
||||||
address = address if address else self._generate_new_address() |
|
||||||
new_account = Account( |
|
||||||
address, |
|
||||||
balance=balance, |
|
||||||
dynamic_loader=dynamic_loader, |
|
||||||
concrete_storage=concrete_storage, |
|
||||||
) |
|
||||||
self._put_account(new_account) |
|
||||||
return new_account |
|
||||||
|
|
||||||
def create_initialized_contract_account(self, contract_code, storage) -> None: |
|
||||||
""" |
|
||||||
Creates a new contract account, based on the contract code and storage provided |
|
||||||
The contract code only includes the runtime contract bytecode |
|
||||||
:param contract_code: Runtime bytecode for the contract |
|
||||||
:param storage: Initial storage for the contract |
|
||||||
:return: The new account |
|
||||||
""" |
|
||||||
# TODO: Add type hints |
|
||||||
new_account = Account( |
|
||||||
self._generate_new_address(), code=contract_code, balance=0 |
|
||||||
) |
|
||||||
new_account.storage = storage |
|
||||||
self._put_account(new_account) |
|
||||||
|
|
||||||
def _generate_new_address(self) -> str: |
|
||||||
""" Generates a new address for the global state""" |
|
||||||
while True: |
|
||||||
address = "0x" + "".join([str(hex(randint(0, 16)))[-1] for _ in range(20)]) |
|
||||||
if address not in self.accounts.keys(): |
|
||||||
return address |
|
||||||
|
|
||||||
def _put_account(self, account: Account) -> None: |
|
||||||
self.accounts[account.address] = account |
|
@ -0,0 +1 @@ |
|||||||
|
# Hello! |
@ -0,0 +1,105 @@ |
|||||||
|
from typing import Dict, Union, Any, KeysView |
||||||
|
|
||||||
|
from z3 import BitVec, ExprRef |
||||||
|
|
||||||
|
from mythril.disassembler.disassembly import Disassembly |
||||||
|
|
||||||
|
|
||||||
|
class Storage: |
||||||
|
""" |
||||||
|
Storage class represents the storage of an Account |
||||||
|
""" |
||||||
|
|
||||||
|
def __init__(self, concrete=False, address=None, dynamic_loader=None): |
||||||
|
""" |
||||||
|
Constructor for Storage |
||||||
|
:param concrete: bool indicating whether to interpret uninitialized storage as concrete versus symbolic |
||||||
|
""" |
||||||
|
self._storage = {} |
||||||
|
self.concrete = concrete |
||||||
|
self.dynld = dynamic_loader |
||||||
|
self.address = address |
||||||
|
|
||||||
|
def __getitem__(self, item: Union[int, slice]) -> Any: |
||||||
|
try: |
||||||
|
return self._storage[item] |
||||||
|
except KeyError: |
||||||
|
if ( |
||||||
|
self.address |
||||||
|
and int(self.address[2:], 16) != 0 |
||||||
|
and (self.dynld and self.dynld.storage_loading) |
||||||
|
): |
||||||
|
try: |
||||||
|
self._storage[item] = int( |
||||||
|
self.dynld.read_storage( |
||||||
|
contract_address=self.address, index=int(item) |
||||||
|
), |
||||||
|
16, |
||||||
|
) |
||||||
|
return self._storage[item] |
||||||
|
except ValueError: |
||||||
|
pass |
||||||
|
if self.concrete: |
||||||
|
return 0 |
||||||
|
self._storage[item] = BitVec("storage_" + str(item), 256) |
||||||
|
return self._storage[item] |
||||||
|
|
||||||
|
def __setitem__(self, key: str, value: ExprRef) -> None: |
||||||
|
self._storage[key] = value |
||||||
|
|
||||||
|
def keys(self) -> KeysView: |
||||||
|
return self._storage.keys() |
||||||
|
|
||||||
|
|
||||||
|
class Account: |
||||||
|
""" |
||||||
|
Account class representing ethereum accounts |
||||||
|
""" |
||||||
|
|
||||||
|
def __init__( |
||||||
|
self, |
||||||
|
address: str, |
||||||
|
code=None, |
||||||
|
contract_name="unknown", |
||||||
|
balance=None, |
||||||
|
concrete_storage=False, |
||||||
|
dynamic_loader=None, |
||||||
|
): |
||||||
|
""" |
||||||
|
Constructor for account |
||||||
|
:param address: Address of the account |
||||||
|
:param code: The contract code of the account |
||||||
|
:param contract_name: The name associated with the account |
||||||
|
:param balance: The balance for the account |
||||||
|
:param concrete_storage: Interpret storage as concrete |
||||||
|
""" |
||||||
|
self.nonce = 0 |
||||||
|
self.code = code or Disassembly("") |
||||||
|
self.balance = balance if balance else BitVec("balance", 256) |
||||||
|
self.storage = Storage( |
||||||
|
concrete_storage, address=address, dynamic_loader=dynamic_loader |
||||||
|
) |
||||||
|
|
||||||
|
# Metadata |
||||||
|
self.address = address |
||||||
|
self.contract_name = contract_name |
||||||
|
|
||||||
|
self.deleted = False |
||||||
|
|
||||||
|
def __str__(self) -> str: |
||||||
|
return str(self.as_dict) |
||||||
|
|
||||||
|
def set_balance(self, balance: ExprRef) -> None: |
||||||
|
self.balance = balance |
||||||
|
|
||||||
|
def add_balance(self, balance: ExprRef) -> None: |
||||||
|
self.balance += balance |
||||||
|
|
||||||
|
@property |
||||||
|
def as_dict(self) -> Dict: |
||||||
|
return { |
||||||
|
"nonce": self.nonce, |
||||||
|
"code": self.code, |
||||||
|
"balance": self.balance, |
||||||
|
"storage": self.storage, |
||||||
|
} |
@ -0,0 +1,104 @@ |
|||||||
|
from enum import Enum |
||||||
|
from typing import Union, Any |
||||||
|
from z3 import ( |
||||||
|
BitVecVal, |
||||||
|
BitVecRef, |
||||||
|
BitVecSort, |
||||||
|
BitVec, |
||||||
|
Implies, |
||||||
|
simplify, |
||||||
|
Concat, |
||||||
|
UGT, |
||||||
|
Array, |
||||||
|
) |
||||||
|
from z3.z3types import Z3Exception |
||||||
|
|
||||||
|
from mythril.laser.ethereum.util import get_concrete_int |
||||||
|
|
||||||
|
|
||||||
|
class CalldataType(Enum): |
||||||
|
CONCRETE = 1 |
||||||
|
SYMBOLIC = 2 |
||||||
|
|
||||||
|
|
||||||
|
class Calldata: |
||||||
|
""" |
||||||
|
Calldata class representing the calldata of a transaction |
||||||
|
""" |
||||||
|
|
||||||
|
def __init__(self, tx_id, starting_calldata=None): |
||||||
|
""" |
||||||
|
Constructor for Calldata |
||||||
|
:param tx_id: unique value representing the transaction the calldata is for |
||||||
|
:param starting_calldata: byte array representing the concrete calldata of a transaction |
||||||
|
""" |
||||||
|
self.tx_id = tx_id |
||||||
|
if starting_calldata is not None: |
||||||
|
self._calldata = [] |
||||||
|
self.calldatasize = BitVecVal(len(starting_calldata), 256) |
||||||
|
self.concrete = True |
||||||
|
else: |
||||||
|
self._calldata = Array( |
||||||
|
"{}_calldata".format(self.tx_id), BitVecSort(256), BitVecSort(8) |
||||||
|
) |
||||||
|
self.calldatasize = BitVec("{}_calldatasize".format(self.tx_id), 256) |
||||||
|
self.concrete = False |
||||||
|
|
||||||
|
if self.concrete: |
||||||
|
for calldata_byte in starting_calldata: |
||||||
|
if type(calldata_byte) == int: |
||||||
|
self._calldata.append(BitVecVal(calldata_byte, 8)) |
||||||
|
else: |
||||||
|
self._calldata.append(calldata_byte) |
||||||
|
|
||||||
|
def concretized(self, model): |
||||||
|
result = [] |
||||||
|
for i in range( |
||||||
|
get_concrete_int(model.eval(self.calldatasize, model_completion=True)) |
||||||
|
): |
||||||
|
result.append( |
||||||
|
get_concrete_int(model.eval(self._calldata[i], model_completion=True)) |
||||||
|
) |
||||||
|
|
||||||
|
return result |
||||||
|
|
||||||
|
def get_word_at(self, index: int): |
||||||
|
return self[index : index + 32] |
||||||
|
|
||||||
|
def __getitem__(self, item: Union[int, slice]) -> Any: |
||||||
|
if isinstance(item, slice): |
||||||
|
start, step, stop = item.start, item.step, item.stop |
||||||
|
try: |
||||||
|
if start is None: |
||||||
|
start = 0 |
||||||
|
if step is None: |
||||||
|
step = 1 |
||||||
|
if stop is None: |
||||||
|
stop = self.calldatasize |
||||||
|
current_index = ( |
||||||
|
start if isinstance(start, BitVecRef) else BitVecVal(start, 256) |
||||||
|
) |
||||||
|
dataparts = [] |
||||||
|
while simplify(current_index != stop): |
||||||
|
dataparts.append(self[current_index]) |
||||||
|
current_index = simplify(current_index + step) |
||||||
|
except Z3Exception: |
||||||
|
raise IndexError("Invalid Calldata Slice") |
||||||
|
|
||||||
|
values, constraints = zip(*dataparts) |
||||||
|
result_constraints = [] |
||||||
|
for c in constraints: |
||||||
|
result_constraints.extend(c) |
||||||
|
return simplify(Concat(values)), result_constraints |
||||||
|
|
||||||
|
if self.concrete: |
||||||
|
try: |
||||||
|
return self._calldata[get_concrete_int(item)], () |
||||||
|
except IndexError: |
||||||
|
return BitVecVal(0, 8), () |
||||||
|
else: |
||||||
|
constraints = [ |
||||||
|
Implies(self._calldata[item] != 0, UGT(self.calldatasize, item)) |
||||||
|
] |
||||||
|
|
||||||
|
return self._calldata[item], constraints |
@ -0,0 +1,35 @@ |
|||||||
|
class Constraints(list): |
||||||
|
""" |
||||||
|
This class should maintain a solver and it's constraints, This class tries to make the Constraints() object |
||||||
|
as a simple list of constraints with some background processing. |
||||||
|
TODO: add the solver to this class after callback refactor |
||||||
|
""" |
||||||
|
|
||||||
|
def __init__(self, constraint_list=None, solver=None, possibility=None): |
||||||
|
super(Constraints, self).__init__(constraint_list or []) |
||||||
|
self.solver = solver |
||||||
|
self.__possibility = possibility |
||||||
|
|
||||||
|
def check_possibility(self): |
||||||
|
return True |
||||||
|
|
||||||
|
def append(self, constraint): |
||||||
|
super(Constraints, self).append(constraint) |
||||||
|
|
||||||
|
def pop(self, index=-1): |
||||||
|
raise NotImplementedError |
||||||
|
|
||||||
|
def __copy__(self): |
||||||
|
constraint_list = super(Constraints, self).copy() |
||||||
|
return Constraints(constraint_list) |
||||||
|
|
||||||
|
def __deepcopy__(self, memodict=None): |
||||||
|
return self.__copy__() |
||||||
|
|
||||||
|
def __add__(self, constraints): |
||||||
|
constraints_list = super(Constraints, self).__add__(constraints) |
||||||
|
return Constraints(constraint_list=constraints_list) |
||||||
|
|
||||||
|
def __iadd__(self, constraints): |
||||||
|
super(Constraints, self).__iadd__(constraints) |
||||||
|
return self |
@ -0,0 +1,55 @@ |
|||||||
|
from typing import Dict |
||||||
|
|
||||||
|
from z3 import ExprRef, BitVecVal |
||||||
|
|
||||||
|
from mythril.laser.ethereum.state.account import Account |
||||||
|
from mythril.laser.ethereum.state.calldata import Calldata, CalldataType |
||||||
|
|
||||||
|
|
||||||
|
class Environment: |
||||||
|
""" |
||||||
|
The environment class represents the current execution environment for the symbolic executor |
||||||
|
""" |
||||||
|
|
||||||
|
def __init__( |
||||||
|
self, |
||||||
|
active_account: Account, |
||||||
|
sender: ExprRef, |
||||||
|
calldata: Calldata, |
||||||
|
gasprice: ExprRef, |
||||||
|
callvalue: ExprRef, |
||||||
|
origin: ExprRef, |
||||||
|
code=None, |
||||||
|
calldata_type=CalldataType.SYMBOLIC, |
||||||
|
): |
||||||
|
# Metadata |
||||||
|
|
||||||
|
self.active_account = active_account |
||||||
|
self.active_function_name = "" |
||||||
|
|
||||||
|
self.address = BitVecVal(int(active_account.address, 16), 256) |
||||||
|
|
||||||
|
# Ib |
||||||
|
self.code = active_account.code if code is None else code |
||||||
|
|
||||||
|
self.sender = sender |
||||||
|
self.calldata = calldata |
||||||
|
self.calldata_type = calldata_type |
||||||
|
self.gasprice = gasprice |
||||||
|
self.origin = origin |
||||||
|
self.callvalue = callvalue |
||||||
|
|
||||||
|
def __str__(self) -> str: |
||||||
|
return str(self.as_dict) |
||||||
|
|
||||||
|
@property |
||||||
|
def as_dict(self) -> Dict: |
||||||
|
return dict( |
||||||
|
active_account=self.active_account, |
||||||
|
sender=self.sender, |
||||||
|
calldata=self.calldata, |
||||||
|
gasprice=self.gasprice, |
||||||
|
callvalue=self.callvalue, |
||||||
|
origin=self.origin, |
||||||
|
calldata_type=self.calldata_type, |
||||||
|
) |
@ -0,0 +1,77 @@ |
|||||||
|
from typing import Dict, Union |
||||||
|
|
||||||
|
from copy import copy, deepcopy |
||||||
|
from z3 import BitVec |
||||||
|
|
||||||
|
from mythril.laser.ethereum.cfg import Node |
||||||
|
from mythril.laser.ethereum.state.environment import Environment |
||||||
|
from mythril.laser.ethereum.state.machine_state import MachineState |
||||||
|
|
||||||
|
|
||||||
|
class GlobalState: |
||||||
|
""" |
||||||
|
GlobalState represents the current globalstate |
||||||
|
""" |
||||||
|
|
||||||
|
def __init__( |
||||||
|
self, |
||||||
|
world_state: "WorldState", |
||||||
|
environment: Environment, |
||||||
|
node: Node, |
||||||
|
machine_state=None, |
||||||
|
transaction_stack=None, |
||||||
|
last_return_data=None, |
||||||
|
): |
||||||
|
""" Constructor for GlobalState""" |
||||||
|
self.node = node |
||||||
|
self.world_state = world_state |
||||||
|
self.environment = environment |
||||||
|
self.mstate = ( |
||||||
|
machine_state if machine_state else MachineState(gas_limit=1000000000) |
||||||
|
) |
||||||
|
self.transaction_stack = transaction_stack if transaction_stack else [] |
||||||
|
self.op_code = "" |
||||||
|
self.last_return_data = last_return_data |
||||||
|
|
||||||
|
def __copy__(self) -> "GlobalState": |
||||||
|
world_state = copy(self.world_state) |
||||||
|
environment = copy(self.environment) |
||||||
|
mstate = deepcopy(self.mstate) |
||||||
|
transaction_stack = copy(self.transaction_stack) |
||||||
|
return GlobalState( |
||||||
|
world_state, |
||||||
|
environment, |
||||||
|
self.node, |
||||||
|
mstate, |
||||||
|
transaction_stack=transaction_stack, |
||||||
|
last_return_data=self.last_return_data, |
||||||
|
) |
||||||
|
|
||||||
|
@property |
||||||
|
def accounts(self) -> Dict: |
||||||
|
return self.world_state.accounts |
||||||
|
|
||||||
|
# TODO: remove this, as two instructions are confusing |
||||||
|
def get_current_instruction(self) -> Dict: |
||||||
|
""" Gets the current instruction for this GlobalState""" |
||||||
|
|
||||||
|
instructions = self.environment.code.instruction_list |
||||||
|
return instructions[self.mstate.pc] |
||||||
|
|
||||||
|
@property |
||||||
|
def current_transaction( |
||||||
|
self |
||||||
|
) -> Union["MessageCallTransaction", "ContractCreationTransaction", None]: |
||||||
|
# TODO: Remove circular to transaction package to import Transaction classes |
||||||
|
try: |
||||||
|
return self.transaction_stack[-1][0] |
||||||
|
except IndexError: |
||||||
|
return None |
||||||
|
|
||||||
|
@property |
||||||
|
def instruction(self) -> Dict: |
||||||
|
return self.get_current_instruction() |
||||||
|
|
||||||
|
def new_bitvec(self, name: str, size=256) -> BitVec: |
||||||
|
transaction_id = self.current_transaction.id |
||||||
|
return BitVec("{}_{}".format(transaction_id, name), size) |
@ -0,0 +1,177 @@ |
|||||||
|
from copy import copy |
||||||
|
from typing import Union, Any, List, Dict |
||||||
|
|
||||||
|
from z3 import BitVec |
||||||
|
|
||||||
|
from ethereum import opcodes, utils |
||||||
|
from mythril.laser.ethereum.evm_exceptions import ( |
||||||
|
StackOverflowException, |
||||||
|
StackUnderflowException, |
||||||
|
OutOfGasException, |
||||||
|
) |
||||||
|
from mythril.laser.ethereum.state.constraints import Constraints |
||||||
|
|
||||||
|
|
||||||
|
class MachineStack(list): |
||||||
|
""" |
||||||
|
Defines EVM stack, overrides the default list to handle overflows |
||||||
|
""" |
||||||
|
|
||||||
|
STACK_LIMIT = 1024 |
||||||
|
|
||||||
|
def __init__(self, default_list=None): |
||||||
|
if default_list is None: |
||||||
|
default_list = [] |
||||||
|
super(MachineStack, self).__init__(default_list) |
||||||
|
|
||||||
|
def append(self, element: BitVec) -> None: |
||||||
|
""" |
||||||
|
:param element: element to be appended to the list |
||||||
|
:function: appends the element to list if the size is less than STACK_LIMIT, else throws an error |
||||||
|
""" |
||||||
|
if super(MachineStack, self).__len__() >= self.STACK_LIMIT: |
||||||
|
raise StackOverflowException( |
||||||
|
"Reached the EVM stack limit of {}, you can't append more " |
||||||
|
"elements".format(self.STACK_LIMIT) |
||||||
|
) |
||||||
|
super(MachineStack, self).append(element) |
||||||
|
|
||||||
|
def pop(self, index=-1) -> BitVec: |
||||||
|
""" |
||||||
|
:param index:index to be popped, same as the list() class. |
||||||
|
:returns popped value |
||||||
|
:function: same as list() class but throws StackUnderflowException for popping from an empty list |
||||||
|
""" |
||||||
|
|
||||||
|
try: |
||||||
|
return super(MachineStack, self).pop(index) |
||||||
|
except IndexError: |
||||||
|
raise StackUnderflowException("Trying to pop from an empty stack") |
||||||
|
|
||||||
|
def __getitem__(self, item: Union[int, slice]) -> Any: |
||||||
|
try: |
||||||
|
return super(MachineStack, self).__getitem__(item) |
||||||
|
except IndexError: |
||||||
|
raise StackUnderflowException( |
||||||
|
"Trying to access a stack element which doesn't exist" |
||||||
|
) |
||||||
|
|
||||||
|
def __add__(self, other): |
||||||
|
""" |
||||||
|
Implement list concatenation if needed |
||||||
|
""" |
||||||
|
raise NotImplementedError("Implement this if needed") |
||||||
|
|
||||||
|
def __iadd__(self, other): |
||||||
|
""" |
||||||
|
Implement list concatenation if needed |
||||||
|
""" |
||||||
|
raise NotImplementedError("Implement this if needed") |
||||||
|
|
||||||
|
|
||||||
|
class MachineState: |
||||||
|
""" |
||||||
|
MachineState represents current machine state also referenced to as \mu |
||||||
|
""" |
||||||
|
|
||||||
|
def __init__( |
||||||
|
self, |
||||||
|
gas_limit: int, |
||||||
|
pc=0, |
||||||
|
stack=None, |
||||||
|
memory=None, |
||||||
|
constraints=None, |
||||||
|
depth=0, |
||||||
|
max_gas_used=0, |
||||||
|
min_gas_used=0, |
||||||
|
): |
||||||
|
""" Constructor for machineState """ |
||||||
|
self.pc = pc |
||||||
|
self.stack = MachineStack(stack) |
||||||
|
self.memory = memory or [] |
||||||
|
self.gas_limit = gas_limit |
||||||
|
self.min_gas_used = min_gas_used # lower gas usage bound |
||||||
|
self.max_gas_used = max_gas_used # upper gas usage bound |
||||||
|
self.constraints = constraints or Constraints() |
||||||
|
self.depth = depth |
||||||
|
|
||||||
|
def calculate_extension_size(self, start: int, size: int) -> int: |
||||||
|
if self.memory_size > start + size: |
||||||
|
return 0 |
||||||
|
return start + size - self.memory_size |
||||||
|
|
||||||
|
def calculate_memory_gas(self, start: int, size: int): |
||||||
|
# https://github.com/ethereum/pyethereum/blob/develop/ethereum/vm.py#L148 |
||||||
|
oldsize = self.memory_size // 32 |
||||||
|
old_totalfee = ( |
||||||
|
oldsize * opcodes.GMEMORY + oldsize ** 2 // opcodes.GQUADRATICMEMDENOM |
||||||
|
) |
||||||
|
newsize = utils.ceil32(start + size) // 32 |
||||||
|
new_totalfee = ( |
||||||
|
newsize * opcodes.GMEMORY + newsize ** 2 // opcodes.GQUADRATICMEMDENOM |
||||||
|
) |
||||||
|
return new_totalfee - old_totalfee |
||||||
|
|
||||||
|
def check_gas(self): |
||||||
|
if self.min_gas_used > self.gas_limit: |
||||||
|
raise OutOfGasException() |
||||||
|
|
||||||
|
def mem_extend(self, start: int, size: int) -> None: |
||||||
|
""" |
||||||
|
Extends the memory of this machine state |
||||||
|
:param start: Start of memory extension |
||||||
|
:param size: Size of memory extension |
||||||
|
""" |
||||||
|
m_extend = self.calculate_extension_size(start, size) |
||||||
|
if m_extend: |
||||||
|
extend_gas = self.calculate_memory_gas(start, size) |
||||||
|
self.min_gas_used += extend_gas |
||||||
|
self.max_gas_used += extend_gas |
||||||
|
self.check_gas() |
||||||
|
self.memory.extend(bytearray(m_extend)) |
||||||
|
|
||||||
|
def memory_write(self, offset: int, data: List[int]) -> None: |
||||||
|
""" Writes data to memory starting at offset """ |
||||||
|
self.mem_extend(offset, len(data)) |
||||||
|
self.memory[offset : offset + len(data)] = data |
||||||
|
|
||||||
|
def pop(self, amount=1) -> Union[BitVec, List[BitVec]]: |
||||||
|
""" Pops amount elements from the stack""" |
||||||
|
if amount > len(self.stack): |
||||||
|
raise StackUnderflowException |
||||||
|
values = self.stack[-amount:][::-1] |
||||||
|
del self.stack[-amount:] |
||||||
|
|
||||||
|
return values[0] if amount == 1 else values |
||||||
|
|
||||||
|
def __deepcopy__(self, memodict=None): |
||||||
|
memodict = {} if memodict is None else memodict |
||||||
|
return MachineState( |
||||||
|
gas_limit=self.gas_limit, |
||||||
|
max_gas_used=self.max_gas_used, |
||||||
|
min_gas_used=self.min_gas_used, |
||||||
|
pc=self.pc, |
||||||
|
stack=copy(self.stack), |
||||||
|
memory=copy(self.memory), |
||||||
|
constraints=copy(self.constraints), |
||||||
|
depth=self.depth, |
||||||
|
) |
||||||
|
|
||||||
|
def __str__(self): |
||||||
|
return str(self.as_dict) |
||||||
|
|
||||||
|
@property |
||||||
|
def memory_size(self) -> int: |
||||||
|
return len(self.memory) |
||||||
|
|
||||||
|
@property |
||||||
|
def as_dict(self) -> Dict: |
||||||
|
return dict( |
||||||
|
pc=self.pc, |
||||||
|
stack=self.stack, |
||||||
|
memory=self.memory, |
||||||
|
memsize=self.memory_size, |
||||||
|
gas=self.gas_limit, |
||||||
|
max_gas_used=self.max_gas_used, |
||||||
|
min_gas_used=self.min_gas_used, |
||||||
|
) |
@ -0,0 +1,78 @@ |
|||||||
|
from copy import copy |
||||||
|
from random import randint |
||||||
|
|
||||||
|
from mythril.laser.ethereum.state.account import Account |
||||||
|
|
||||||
|
|
||||||
|
class WorldState: |
||||||
|
""" |
||||||
|
The WorldState class represents the world state as described in the yellow paper |
||||||
|
""" |
||||||
|
|
||||||
|
def __init__(self, transaction_sequence=None): |
||||||
|
""" |
||||||
|
Constructor for the world state. Initializes the accounts record |
||||||
|
""" |
||||||
|
self.accounts = {} |
||||||
|
self.node = None |
||||||
|
self.transaction_sequence = transaction_sequence or [] |
||||||
|
|
||||||
|
def __getitem__(self, item: str) -> Account: |
||||||
|
""" |
||||||
|
Gets an account from the worldstate using item as key |
||||||
|
:param item: Address of the account to get |
||||||
|
:return: Account associated with the address |
||||||
|
""" |
||||||
|
return self.accounts[item] |
||||||
|
|
||||||
|
def __copy__(self) -> "WorldState": |
||||||
|
new_world_state = WorldState(transaction_sequence=self.transaction_sequence[:]) |
||||||
|
new_world_state.accounts = copy(self.accounts) |
||||||
|
new_world_state.node = self.node |
||||||
|
return new_world_state |
||||||
|
|
||||||
|
def create_account( |
||||||
|
self, balance=0, address=None, concrete_storage=False, dynamic_loader=None |
||||||
|
) -> Account: |
||||||
|
""" |
||||||
|
Create non-contract account |
||||||
|
:param address: The account's address |
||||||
|
:param balance: Initial balance for the account |
||||||
|
:param concrete_storage: Interpret account storage as concrete |
||||||
|
:param dynamic_loader: used for dynamically loading storage from the block chain |
||||||
|
:return: The new account |
||||||
|
""" |
||||||
|
address = address if address else self._generate_new_address() |
||||||
|
new_account = Account( |
||||||
|
address, |
||||||
|
balance=balance, |
||||||
|
dynamic_loader=dynamic_loader, |
||||||
|
concrete_storage=concrete_storage, |
||||||
|
) |
||||||
|
self._put_account(new_account) |
||||||
|
return new_account |
||||||
|
|
||||||
|
def create_initialized_contract_account(self, contract_code, storage) -> None: |
||||||
|
""" |
||||||
|
Creates a new contract account, based on the contract code and storage provided |
||||||
|
The contract code only includes the runtime contract bytecode |
||||||
|
:param contract_code: Runtime bytecode for the contract |
||||||
|
:param storage: Initial storage for the contract |
||||||
|
:return: The new account |
||||||
|
""" |
||||||
|
# TODO: Add type hints |
||||||
|
new_account = Account( |
||||||
|
self._generate_new_address(), code=contract_code, balance=0 |
||||||
|
) |
||||||
|
new_account.storage = storage |
||||||
|
self._put_account(new_account) |
||||||
|
|
||||||
|
def _generate_new_address(self) -> str: |
||||||
|
""" Generates a new address for the global state""" |
||||||
|
while True: |
||||||
|
address = "0x" + "".join([str(hex(randint(0, 16)))[-1] for _ in range(20)]) |
||||||
|
if address not in self.accounts.keys(): |
||||||
|
return address |
||||||
|
|
||||||
|
def _put_account(self, account: Account) -> None: |
||||||
|
self.accounts[account.address] = account |
@ -1,5 +1,5 @@ |
|||||||
{ |
{ |
||||||
"calldatacopyUnderFlow" : { |
"calldatacopyUnderFlowerror" : { |
||||||
"_info" : { |
"_info" : { |
||||||
"comment" : "", |
"comment" : "", |
||||||
"filledwith" : "testeth 1.5.0.dev2-52+commit.d419e0a2", |
"filledwith" : "testeth 1.5.0.dev2-52+commit.d419e0a2", |
@ -1,5 +1,5 @@ |
|||||||
{ |
{ |
||||||
"sha3_3" : { |
"sha3_3oog" : { |
||||||
"_info" : { |
"_info" : { |
||||||
"comment" : "", |
"comment" : "", |
||||||
"filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++", |
"filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++", |
@ -1,5 +1,5 @@ |
|||||||
{ |
{ |
||||||
"sha3_4" : { |
"sha3_4oog" : { |
||||||
"_info" : { |
"_info" : { |
||||||
"comment" : "", |
"comment" : "", |
||||||
"filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++", |
"filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++", |
@ -1,5 +1,5 @@ |
|||||||
{ |
{ |
||||||
"sha3_5" : { |
"sha3_5oog" : { |
||||||
"_info" : { |
"_info" : { |
||||||
"comment" : "", |
"comment" : "", |
||||||
"filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++", |
"filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++", |
@ -1,5 +1,5 @@ |
|||||||
{ |
{ |
||||||
"sha3_6" : { |
"sha3_6oog" : { |
||||||
"_info" : { |
"_info" : { |
||||||
"comment" : "", |
"comment" : "", |
||||||
"filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++", |
"filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++", |
@ -1,5 +1,5 @@ |
|||||||
{ |
{ |
||||||
"sha3_bigOffset" : { |
"sha3_bigOffsetoog" : { |
||||||
"_info" : { |
"_info" : { |
||||||
"comment" : "", |
"comment" : "", |
||||||
"filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++", |
"filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++", |
@ -1,5 +1,5 @@ |
|||||||
{ |
{ |
||||||
"sha3_bigSize" : { |
"sha3_bigSizeoog" : { |
||||||
"_info" : { |
"_info" : { |
||||||
"comment" : "", |
"comment" : "", |
||||||
"filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++", |
"filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++", |
@ -1 +1,126 @@ |
|||||||
{"error": null, "issues": [{"address": 661, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code.", "function": "_function_0x5a6814ec", "swc-id": "107", "title": "Message call to external contract", "type": "Informational"}, {"address": 666, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "_function_0x5a6814ec", "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}, {"address": 779, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "This contract executes a message call to an address found at storage slot 1. This storage slot can be written to by calling the function `_function_0x2776b163`. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.", "function": "_function_0xd24b08cc", "swc-id": "107", "title": "Message call to external contract", "type": "Warning"}, {"address": 779, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "Possible transaction order dependence vulnerability: The value or direction of the call statement is determined from a tainted storage location", "function": "_function_0xd24b08cc", "swc-id": "114", "title": "Transaction order dependence", "type": "Warning"}, {"address": 784, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "_function_0xd24b08cc", "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}, {"address": 858, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code.", "function": "_function_0xe11f493e", "swc-id": "107", "title": "Message call to external contract", "type": "Informational"}, {"address": 869, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The contract account state is changed after an external call. Consider that the called contract could re-enter the function before this state change takes place. This can lead to business logic vulnerabilities.", "function": "_function_0xe11f493e", "swc-id": "107", "title": "State change after external call", "type": "Warning"}, {"address": 871, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "_function_0xe11f493e", "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}, {"address": 912, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.", "function": "_function_0xe1d10f79", "swc-id": "107", "title": "Message call to external contract", "type": "Warning"}, {"address": 918, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "_function_0xe1d10f79", "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}], "success": true} |
{ |
||||||
|
"error": null, |
||||||
|
"issues": [ |
||||||
|
{ |
||||||
|
"address": 661, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code.", |
||||||
|
"function": "_function_0x5a6814ec", |
||||||
|
"swc-id": "107", |
||||||
|
"min_gas_used": 643, |
||||||
|
"max_gas_used": 1254, |
||||||
|
"title": "Message call to external contract", |
||||||
|
"type": "Informational" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 666, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", |
||||||
|
"function": "_function_0x5a6814ec", |
||||||
|
"swc-id": "104", |
||||||
|
"min_gas_used": 1352, |
||||||
|
"max_gas_used": 35963, |
||||||
|
"title": "Unchecked CALL return value", |
||||||
|
"type": "Informational" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 779, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "This contract executes a message call to an address found at storage slot 1. This storage slot can be written to by calling the function `_function_0x2776b163`. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.", |
||||||
|
"function": "_function_0xd24b08cc", |
||||||
|
"swc-id": "107", |
||||||
|
"min_gas_used": 687, |
||||||
|
"max_gas_used": 1298, |
||||||
|
"title": "Message call to external contract", |
||||||
|
"type": "Warning" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 779, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "Possible transaction order dependence vulnerability: The value or direction of the call statement is determined from a tainted storage location", |
||||||
|
"function": "_function_0xd24b08cc", |
||||||
|
"swc-id": "114", |
||||||
|
"min_gas_used": 687, |
||||||
|
"max_gas_used": 1298, |
||||||
|
"title": "Transaction order dependence", |
||||||
|
"type": "Warning" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 784, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", |
||||||
|
"function": "_function_0xd24b08cc", |
||||||
|
"swc-id": "104", |
||||||
|
"min_gas_used": 1396, |
||||||
|
"max_gas_used": 36007, |
||||||
|
"title": "Unchecked CALL return value", |
||||||
|
"type": "Informational" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 858, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code.", |
||||||
|
"function": "_function_0xe11f493e", |
||||||
|
"swc-id": "107", |
||||||
|
"min_gas_used": 709, |
||||||
|
"max_gas_used": 1320, |
||||||
|
"title": "Message call to external contract", |
||||||
|
"type": "Informational" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 869, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "The contract account state is changed after an external call. Consider that the called contract could re-enter the function before this state change takes place. This can lead to business logic vulnerabilities.", |
||||||
|
"function": "_function_0xe11f493e", |
||||||
|
"swc-id": "107", |
||||||
|
"min_gas_used": 709, |
||||||
|
"max_gas_used": 1320, |
||||||
|
"title": "State change after external call", |
||||||
|
"type": "Warning" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 871, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", |
||||||
|
"function": "_function_0xe11f493e", |
||||||
|
"swc-id": "104", |
||||||
|
"min_gas_used": 6432, |
||||||
|
"max_gas_used": 61043, |
||||||
|
"title": "Unchecked CALL return value", |
||||||
|
"type": "Informational" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 912, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.", |
||||||
|
"function": "_function_0xe1d10f79", |
||||||
|
"swc-id": "107", |
||||||
|
"min_gas_used": 335, |
||||||
|
"max_gas_used": 616, |
||||||
|
"title": "Message call to external contract", |
||||||
|
"type": "Warning" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 918, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", |
||||||
|
"function": "_function_0xe1d10f79", |
||||||
|
"swc-id": "104", |
||||||
|
"min_gas_used": 1046, |
||||||
|
"max_gas_used": 35327, |
||||||
|
"title": "Unchecked CALL return value", |
||||||
|
"type": "Informational" |
||||||
|
} |
||||||
|
], |
||||||
|
"success": true |
||||||
|
} |
||||||
|
@ -1 +1,36 @@ |
|||||||
{"error": null, "issues": [{"address": 158, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The arithmetic operation can result in integer overflow.\n", "function": "_function_0x83f12fec", "swc-id": "101", "title": "Integer Overflow", "type": "Warning"}, {"address": 278, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The arithmetic operation can result in integer overflow.\n", "function": "_function_0x83f12fec", "swc-id": "101", "title": "Integer Overflow", "type": "Warning"}, {"address": 378, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The substraction can result in an integer underflow.\n", "function": "_function_0x83f12fec", "swc-id": "101", "title": "Integer Underflow", "type": "Warning"}], "success": true} |
{ |
||||||
|
"error": null, |
||||||
|
"issues": [ |
||||||
|
{ |
||||||
|
"address": 158, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "The arithmetic operation can result in integer overflow.\n", |
||||||
|
"function": "_function_0x83f12fec", |
||||||
|
"swc-id": "101", |
||||||
|
"title": "Integer Overflow", |
||||||
|
"type": "Warning" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 278, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "The arithmetic operation can result in integer overflow.\n", |
||||||
|
"function": "_function_0x83f12fec", |
||||||
|
"swc-id": "101", |
||||||
|
"title": "Integer Overflow", |
||||||
|
"type": "Warning" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 378, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "The substraction can result in an integer underflow.\n", |
||||||
|
"function": "_function_0x83f12fec", |
||||||
|
"swc-id": "101", |
||||||
|
"title": "Integer Underflow", |
||||||
|
"type": "Warning" |
||||||
|
} |
||||||
|
], |
||||||
|
"success": true |
||||||
|
} |
||||||
|
@ -1 +1,30 @@ |
|||||||
{"error": null, "issues": [{"address": 722, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "Users other than the contract creator can withdraw ETH from the contract account without previously having sent any ETH to it. This is likely to be vulnerability.", "function": "withdrawfunds()", "swc-id": "105", "title": "Ether thief", "type": "Warning"}, {"address": 883, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "This binary add operation can result in integer overflow.\n", "function": "invest()", "swc-id": "101", "title": "Integer Overflow", "type": "Warning"}], "success": true} |
{ |
||||||
|
"error": null, |
||||||
|
"issues": [ |
||||||
|
{ |
||||||
|
"address": 722, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "Users other than the contract creator can withdraw ETH from the contract account without previously having sent any ETH to it. This is likely to be vulnerability.", |
||||||
|
"function": "withdrawfunds()", |
||||||
|
"swc-id": "105", |
||||||
|
"min_gas_used": 1138, |
||||||
|
"max_gas_used": 1749, |
||||||
|
"title": "Ether thief", |
||||||
|
"type": "Warning" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 883, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "This binary add operation can result in integer overflow.\n", |
||||||
|
"function": "invest()", |
||||||
|
"swc-id": "101", |
||||||
|
"min_gas_used": 1571, |
||||||
|
"max_gas_used": 1856, |
||||||
|
"title": "Integer Overflow", |
||||||
|
"type": "Warning" |
||||||
|
} |
||||||
|
], |
||||||
|
"success": true |
||||||
|
} |
||||||
|
@ -1 +1,54 @@ |
|||||||
{"error": null, "issues": [{"address": 446, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "A reachable exception (opcode 0xfe) has been detected. This 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. ", "function": "_function_0x546455b5", "swc-id": "110", "title": "Exception state", "type": "Informational"}, {"address": 484, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "A reachable exception (opcode 0xfe) has been detected. This 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. ", "function": "_function_0x92dd38ea", "swc-id": "110", "title": "Exception state", "type": "Informational"}, {"address": 506, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "A reachable exception (opcode 0xfe) has been detected. This 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. ", "function": "_function_0xa08299f1", "swc-id": "110", "title": "Exception state", "type": "Informational"}, {"address": 531, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "A reachable exception (opcode 0xfe) has been detected. This 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. ", "function": "_function_0xb34c3610", "swc-id": "110", "title": "Exception state", "type": "Informational"}], "success": true} |
{ |
||||||
|
"error": null, |
||||||
|
"issues": [ |
||||||
|
{ |
||||||
|
"address": 446, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "A reachable exception (opcode 0xfe) has been detected. This 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. ", |
||||||
|
"function": "_function_0x546455b5", |
||||||
|
"swc-id": "110", |
||||||
|
"min_gas_used": 206, |
||||||
|
"max_gas_used": 301, |
||||||
|
"title": "Exception state", |
||||||
|
"type": "Informational" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 484, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "A reachable exception (opcode 0xfe) has been detected. This 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. ", |
||||||
|
"function": "_function_0x92dd38ea", |
||||||
|
"swc-id": "110", |
||||||
|
"min_gas_used": 256, |
||||||
|
"max_gas_used": 351, |
||||||
|
"title": "Exception state", |
||||||
|
"type": "Informational" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 506, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "A reachable exception (opcode 0xfe) has been detected. This 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. ", |
||||||
|
"function": "_function_0xa08299f1", |
||||||
|
"swc-id": "110", |
||||||
|
"min_gas_used": 272, |
||||||
|
"max_gas_used": 367, |
||||||
|
"title": "Exception state", |
||||||
|
"type": "Informational" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 531, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "A reachable exception (opcode 0xfe) has been detected. This 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. ", |
||||||
|
"function": "_function_0xb34c3610", |
||||||
|
"swc-id": "110", |
||||||
|
"min_gas_used": 268, |
||||||
|
"max_gas_used": 363, |
||||||
|
"title": "Exception state", |
||||||
|
"type": "Informational" |
||||||
|
} |
||||||
|
], |
||||||
|
"success": true |
||||||
|
} |
||||||
|
@ -1 +1,54 @@ |
|||||||
{"error": null, "issues": [{"address": 626, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "_function_0x141f32ff", "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}, {"address": 857, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "_function_0x9b58bc26", "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}, {"address": 1038, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.", "function": "_function_0xeea4c864", "swc-id": "107", "title": "Message call to external contract", "type": "Warning"}, {"address": 1046, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "_function_0xeea4c864", "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}], "success": true} |
{ |
||||||
|
"error": null, |
||||||
|
"issues": [ |
||||||
|
{ |
||||||
|
"address": 626, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", |
||||||
|
"function": "_function_0x141f32ff", |
||||||
|
"swc-id": "104", |
||||||
|
"min_gas_used": 1104, |
||||||
|
"max_gas_used": 35856, |
||||||
|
"title": "Unchecked CALL return value", |
||||||
|
"type": "Informational" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 857, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", |
||||||
|
"function": "_function_0x9b58bc26", |
||||||
|
"swc-id": "104", |
||||||
|
"min_gas_used": 1167, |
||||||
|
"max_gas_used": 35919, |
||||||
|
"title": "Unchecked CALL return value", |
||||||
|
"type": "Informational" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 1038, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.", |
||||||
|
"function": "_function_0xeea4c864", |
||||||
|
"swc-id": "107", |
||||||
|
"min_gas_used": 477, |
||||||
|
"max_gas_used": 1229, |
||||||
|
"title": "Message call to external contract", |
||||||
|
"type": "Warning" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 1046, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", |
||||||
|
"function": "_function_0xeea4c864", |
||||||
|
"swc-id": "104", |
||||||
|
"min_gas_used": 1192, |
||||||
|
"max_gas_used": 35944, |
||||||
|
"title": "Unchecked CALL return value", |
||||||
|
"type": "Informational" |
||||||
|
} |
||||||
|
], |
||||||
|
"success": true |
||||||
|
} |
||||||
|
@ -1 +1,5 @@ |
|||||||
{"error": null, "issues": [], "success": true} |
{ |
||||||
|
"error": null, |
||||||
|
"issues": [], |
||||||
|
"success": true |
||||||
|
} |
||||||
|
@ -1 +1,18 @@ |
|||||||
{"error": null, "issues": [{"address": 142, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "Users other than the contract creator can withdraw ETH from the contract account without previously having sent any ETH to it. This is likely to be vulnerability.", "function": "_function_0x8a4068dd", "swc-id": "105", "title": "Ether thief", "type": "Warning"}], "success": true} |
{ |
||||||
|
"error": null, |
||||||
|
"issues": [ |
||||||
|
{ |
||||||
|
"address": 142, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "Users other than the contract creator can withdraw ETH from the contract account without previously having sent any ETH to it. This is likely to be vulnerability.", |
||||||
|
"function": "_function_0x8a4068dd", |
||||||
|
"swc-id": "105", |
||||||
|
"min_gas_used": 186, |
||||||
|
"max_gas_used": 467, |
||||||
|
"title": "Ether thief", |
||||||
|
"type": "Warning" |
||||||
|
} |
||||||
|
], |
||||||
|
"success": true |
||||||
|
} |
||||||
|
@ -1 +1,5 @@ |
|||||||
{"error": null, "issues": [], "success": true} |
{ |
||||||
|
"error": null, |
||||||
|
"issues": [], |
||||||
|
"success": true |
||||||
|
} |
||||||
|
@ -1 +1,18 @@ |
|||||||
{"error": null, "issues": [{"address": 317, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The function `transferOwnership(address)` retrieves the transaction origin (tx.origin) using the ORIGIN opcode. Use msg.sender instead.\nSee also: https://solidity.readthedocs.io/en/develop/security-considerations.html#tx-origin", "function": "transferOwnership(address)", "swc-id": "115", "title": "Use of tx.origin", "type": "Warning"}], "success": true} |
{ |
||||||
|
"error": null, |
||||||
|
"issues": [ |
||||||
|
{ |
||||||
|
"address": 317, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "The function `transferOwnership(address)` retrieves the transaction origin (tx.origin) using the ORIGIN opcode. Use msg.sender instead.\nSee also: https://solidity.readthedocs.io/en/develop/security-considerations.html#tx-origin", |
||||||
|
"function": "transferOwnership(address)", |
||||||
|
"swc-id": "115", |
||||||
|
"min_gas_used": 626, |
||||||
|
"max_gas_used": 1051, |
||||||
|
"title": "Use of tx.origin", |
||||||
|
"type": "Warning" |
||||||
|
} |
||||||
|
], |
||||||
|
"success": true |
||||||
|
} |
||||||
|
@ -1 +1,42 @@ |
|||||||
{"error":null,"issues":[{"address":567,"contract":"Unknown","debug":"<DEBUG-DATA>","description":"The subtraction can result in an integer underflow.\n","function":"sendeth(address,uint256)","swc-id":"101","title":"Integer Underflow","type":"Warning"},{"address":649,"contract":"Unknown","debug":"<DEBUG-DATA>","description":"The subtraction can result in an integer underflow.\n","function":"sendeth(address,uint256)","swc-id":"101","title":"Integer Underflow","type":"Warning"},{"address":725,"contract":"Unknown","debug":"<DEBUG-DATA>","description":"This binary add operation can result in integer overflow.\n","function":"sendeth(address,uint256)","swc-id":"101","title":"Integer Overflow","type":"Warning"}],"success":true} |
{ |
||||||
|
"error": null, |
||||||
|
"issues": [ |
||||||
|
{ |
||||||
|
"address": 567, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "The subtraction can result in an integer underflow.\n", |
||||||
|
"function": "sendeth(address,uint256)", |
||||||
|
"swc-id": "101", |
||||||
|
"min_gas_used": 750, |
||||||
|
"max_gas_used": 1035, |
||||||
|
"title": "Integer Underflow", |
||||||
|
"type": "Warning" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 649, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "The subtraction can result in an integer underflow.\n", |
||||||
|
"function": "sendeth(address,uint256)", |
||||||
|
"swc-id": "101", |
||||||
|
"min_gas_used": 1283, |
||||||
|
"max_gas_used": 1758, |
||||||
|
"title": "Integer Underflow", |
||||||
|
"type": "Warning" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 725, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "This binary add operation can result in integer overflow.\n", |
||||||
|
"function": "sendeth(address,uint256)", |
||||||
|
"swc-id": "101", |
||||||
|
"min_gas_used": 6806, |
||||||
|
"max_gas_used": 27471, |
||||||
|
"title": "Integer Overflow", |
||||||
|
"type": "Warning" |
||||||
|
} |
||||||
|
], |
||||||
|
"success": true |
||||||
|
} |
||||||
|
@ -1 +1,42 @@ |
|||||||
{"error": null, "issues": [{"address": 196, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code.", "function": "_function_0x633ab5e0", "swc-id": "107", "title": "Message call to external contract", "type": "Informational"}, {"address": 285, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code.", "function": "_function_0xe3bea282", "swc-id": "107", "title": "Message call to external contract", "type": "Informational"}, {"address": 290, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "_function_0xe3bea282", "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}], "success": true} |
{ |
||||||
|
"error": null, |
||||||
|
"issues": [ |
||||||
|
{ |
||||||
|
"address": 196, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code.", |
||||||
|
"function": "_function_0x633ab5e0", |
||||||
|
"swc-id": "107", |
||||||
|
"min_gas_used": 599, |
||||||
|
"max_gas_used": 1210, |
||||||
|
"title": "Message call to external contract", |
||||||
|
"type": "Informational" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 285, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code.", |
||||||
|
"function": "_function_0xe3bea282", |
||||||
|
"swc-id": "107", |
||||||
|
"min_gas_used": 621, |
||||||
|
"max_gas_used": 1232, |
||||||
|
"title": "Message call to external contract", |
||||||
|
"type": "Informational" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 290, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", |
||||||
|
"function": "_function_0xe3bea282", |
||||||
|
"swc-id": "104", |
||||||
|
"min_gas_used": 1330, |
||||||
|
"max_gas_used": 35941, |
||||||
|
"title": "Unchecked CALL return value", |
||||||
|
"type": "Informational" |
||||||
|
} |
||||||
|
], |
||||||
|
"success": true |
||||||
|
} |
||||||
|
@ -1 +1,18 @@ |
|||||||
{"error": null, "issues": [{"address": 146, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "A reachable SUICIDE instruction was detected. The remaining Ether is sent to an address provided as a function argument.\n", "function": "_function_0xcbf0b0c0", "swc-id": "106", "title": "Unchecked SUICIDE", "type": "Warning"}], "success": true} |
{ |
||||||
|
"error": null, |
||||||
|
"issues": [ |
||||||
|
{ |
||||||
|
"address": 146, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "A reachable SUICIDE instruction was detected. The remaining Ether is sent to an address provided as a function argument.\n", |
||||||
|
"function": "_function_0xcbf0b0c0", |
||||||
|
"swc-id": "106", |
||||||
|
"min_gas_used": 168, |
||||||
|
"max_gas_used": 263, |
||||||
|
"title": "Unchecked SUICIDE", |
||||||
|
"type": "Warning" |
||||||
|
} |
||||||
|
], |
||||||
|
"success": true |
||||||
|
} |
||||||
|
@ -1 +1,42 @@ |
|||||||
{"error": null, "issues": [{"address": 567, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The subtraction can result in an integer underflow.\n", "function": "sendeth(address,uint256)", "swc-id": "101", "title": "Integer Underflow", "type": "Warning"}, {"address": 649, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "The subtraction can result in an integer underflow.\n", "function": "sendeth(address,uint256)", "swc-id": "101", "title": "Integer Underflow", "type": "Warning"}, {"address": 725, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "This binary add operation can result in integer overflow.\n", "function": "sendeth(address,uint256)", "swc-id": "101", "title": "Integer Overflow", "type": "Warning"}], "success": true} |
{ |
||||||
|
"error": null, |
||||||
|
"issues": [ |
||||||
|
{ |
||||||
|
"address": 567, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "The subtraction can result in an integer underflow.\n", |
||||||
|
"function": "sendeth(address,uint256)", |
||||||
|
"swc-id": "101", |
||||||
|
"min_gas_used": 750, |
||||||
|
"max_gas_used": 1035, |
||||||
|
"title": "Integer Underflow", |
||||||
|
"type": "Warning" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 649, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "The subtraction can result in an integer underflow.\n", |
||||||
|
"function": "sendeth(address,uint256)", |
||||||
|
"swc-id": "101", |
||||||
|
"min_gas_used": 1283, |
||||||
|
"max_gas_used": 1758, |
||||||
|
"title": "Integer Underflow", |
||||||
|
"type": "Warning" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"address": 725, |
||||||
|
"contract": "Unknown", |
||||||
|
"debug": "<DEBUG-DATA>", |
||||||
|
"description": "This binary add operation can result in integer overflow.\n", |
||||||
|
"function": "sendeth(address,uint256)", |
||||||
|
"swc-id": "101", |
||||||
|
"min_gas_used": 6806, |
||||||
|
"max_gas_used": 27471, |
||||||
|
"title": "Integer Overflow", |
||||||
|
"type": "Warning" |
||||||
|
} |
||||||
|
], |
||||||
|
"success": true |
||||||
|
} |
||||||
|
Loading…
Reference in new issue