From a9671de8a3751d916a4190c56879c712a3f02c61 Mon Sep 17 00:00:00 2001 From: JoranHonig Date: Thu, 16 May 2019 09:42:00 +0200 Subject: [PATCH] Balance modelling and symbolic sender variables (#1025) * add actor address to symbolic This will allow us to simulate semi-symbolic transaction senders * add value transfer to transaction global state creation * add proper balance tracking to world state and account * use address value vs string * disable actor address variable * use address directly * allow balance functions with int types * use value as getters since bitvecs aren't hashable * implement correct value transfer for suicide * use actor from actor pool * allow use of Or with *arg pattern * use bitvec instead of strings * add dynamic balance implementation to state datamodels * cleanup svm interface * use balance lambda in suicide op implementation * use bitvec instead of string address * update world state and account creation in symbolic * update tests to include overflow results * apply style rules * ignore previous open states for now * update native test to conform to new laser interface * fix incorrect types in the symbolic virtual machine * allow multiple types as input for address in account * fix type hint in symbolic.py * get int out of string in call * fix type in call op implementation * adapt test_transaction to conform to new laser interface * use static address to keep contracts from trying to enter themselves and make address type more dynamic * update evm test to conform to new laser interface * implement suicide to uncreated account * apply style rules * dynamically create colormap This removes the need for a globally maintained accountlist * get value out of address * add accounts getter * change symbolic test call to be valid with respect to the world state and account apis * remove dependency on globally recorded accounts * fix typing issues * fix type annotations in symbolic.py * fix remaining mypy warnings * simplify assertion check * execute lambda to get balance * make variable name plural * add documentation to svm constructor * use list comprehension to make code cleaner * remove comment * change variable name to plural * remove commented code * change variable name to conform to changed interface --- mythril/analysis/callgraph.py | 23 +- mythril/analysis/symbolic.py | 53 ++-- mythril/laser/ethereum/call.py | 20 +- mythril/laser/ethereum/instructions.py | 28 +- mythril/laser/ethereum/state/account.py | 46 ++-- mythril/laser/ethereum/state/environment.py | 2 +- mythril/laser/ethereum/state/global_state.py | 2 +- mythril/laser/ethereum/state/world_state.py | 57 ++-- mythril/laser/ethereum/svm.py | 89 +++---- .../laser/ethereum/transaction/symbolic.py | 31 ++- .../transaction/transaction_models.py | 16 +- mythril/laser/smt/bool.py | 7 +- tests/laser/evm_testsuite/evm_test.py | 23 +- tests/laser/test_transaction.py | 12 +- tests/laser/transaction/symbolic_test.py | 6 +- tests/native_test.py | 19 +- tests/report_test.py | 2 +- .../outputs_expected/calls.sol.o.json | 244 +++++++++--------- .../outputs_expected/calls.sol.o.jsonv2 | 2 +- .../outputs_expected/environments.sol.o.json | 36 --- .../environments.sol.o.markdown | 34 --- .../outputs_expected/environments.sol.o.text | 30 --- .../outputs_expected/ether_send.sol.o.jsonv2 | 12 +- .../kinds_of_calls.sol.o.jsonv2 | 2 +- .../outputs_expected/metacoin.sol.o.jsonv2 | 12 +- .../outputs_expected/nonascii.sol.o.jsonv2 | 12 +- .../outputs_expected/overflow.sol.o.json | 13 + .../outputs_expected/overflow.sol.o.jsonv2 | 17 ++ .../outputs_expected/overflow.sol.o.markdown | 13 + .../outputs_expected/overflow.sol.o.text | 11 + .../outputs_expected/rubixi.sol.o.json | 166 ------------ .../outputs_expected/rubixi.sol.o.markdown | 238 ----------------- .../outputs_expected/rubixi.sol.o.text | 177 ------------- .../outputs_expected/underflow.sol.o.json | 13 + .../outputs_expected/underflow.sol.o.jsonv2 | 17 ++ .../outputs_expected/underflow.sol.o.markdown | 13 + .../outputs_expected/underflow.sol.o.text | 11 + .../outputs_expected/weak_random.sol.o.json | 46 ---- .../weak_random.sol.o.markdown | 62 ----- .../outputs_expected/weak_random.sol.o.text | 46 ---- 40 files changed, 509 insertions(+), 1154 deletions(-) delete mode 100644 tests/testdata/outputs_expected/environments.sol.o.json delete mode 100644 tests/testdata/outputs_expected/environments.sol.o.markdown delete mode 100644 tests/testdata/outputs_expected/environments.sol.o.text delete mode 100644 tests/testdata/outputs_expected/rubixi.sol.o.json delete mode 100644 tests/testdata/outputs_expected/rubixi.sol.o.markdown delete mode 100644 tests/testdata/outputs_expected/rubixi.sol.o.text delete mode 100644 tests/testdata/outputs_expected/weak_random.sol.o.json delete mode 100644 tests/testdata/outputs_expected/weak_random.sol.o.markdown delete mode 100644 tests/testdata/outputs_expected/weak_random.sol.o.text diff --git a/mythril/analysis/callgraph.py b/mythril/analysis/callgraph.py index b02a62fa..dcbd0216 100644 --- a/mythril/analysis/callgraph.py +++ b/mythril/analysis/callgraph.py @@ -125,7 +125,7 @@ phrack_color = { } -def extract_nodes(statespace, color_map): +def extract_nodes(statespace): """ :param statespace: @@ -133,6 +133,7 @@ def extract_nodes(statespace, color_map): :return: """ nodes = [] + color_map = {} for node_key in statespace.nodes: node = statespace.nodes[node_key] instructions = [state.get_current_instruction() for state in node.states] @@ -164,10 +165,16 @@ def extract_nodes(statespace, color_map): else "\n".join(code_split[:6]) + "\n(click to expand +)" ) + if node.get_cfg_dict()["contract_name"] not in color_map.keys(): + color = default_colors[len(color_map) % len(default_colors)] + color_map[node.get_cfg_dict()["contract_name"]] = color + nodes.append( { "id": str(node_key), - "color": color_map[node.get_cfg_dict()["contract_name"]], + "color": color_map.get( + node.get_cfg_dict()["contract_name"], default_colors[0] + ), "size": 150, "fullLabel": "\n".join(code_split), "label": truncated_code, @@ -231,22 +238,12 @@ def generate_graph( template = env.get_template("callgraph.html") graph_opts = default_opts - accounts = statespace.accounts - - if phrackify: - color_map = {accounts[k].contract_name: phrack_color for k in accounts} - graph_opts.update(phrack_opts) - else: - color_map = { - accounts[k].contract_name: default_colors[i % len(default_colors)] - for i, k in enumerate(accounts) - } graph_opts["physics"]["enabled"] = physics return template.render( title=title, - nodes=extract_nodes(statespace, color_map), + nodes=extract_nodes(statespace), edges=extract_edges(statespace), phrackify=phrackify, opts=graph_opts, diff --git a/mythril/analysis/symbolic.py b/mythril/analysis/symbolic.py index c195a498..cdcb9070 100644 --- a/mythril/analysis/symbolic.py +++ b/mythril/analysis/symbolic.py @@ -2,22 +2,22 @@ purposes.""" import copy -from ethereum.utils import mk_contract_address + from mythril.analysis.security import get_detection_module_hooks, get_detection_modules from mythril.laser.ethereum import svm +from mythril.laser.ethereum.plugins.plugin_factory import PluginFactory +from mythril.laser.ethereum.plugins.plugin_loader import LaserPluginLoader from mythril.laser.ethereum.state.account import Account +from mythril.laser.ethereum.state.world_state import WorldState from mythril.laser.ethereum.strategy.basic import ( BreadthFirstSearchStrategy, DepthFirstSearchStrategy, ReturnRandomNaivelyStrategy, ReturnWeightedRandomStrategy, + BasicSearchStrategy, ) -from mythril.laser.ethereum.transaction.symbolic import CREATOR_ADDRESS - - -from mythril.laser.ethereum.plugins.plugin_factory import PluginFactory -from mythril.laser.ethereum.plugins.plugin_loader import LaserPluginLoader - +from mythril.laser.smt import symbol_factory, BitVec +from typing import Union, List, Dict, Type from mythril.solidity.soliditycontract import EVMContract, SolidityContract from .ops import Call, SStore, VarType, get_variable @@ -32,7 +32,7 @@ class SymExecWrapper: def __init__( self, contract, - address, + address: Union[int, str, BitVec], strategy, dynloader=None, max_depth=22, @@ -55,8 +55,13 @@ class SymExecWrapper: :param transaction_count: :param modules: """ + if isinstance(address, str): + address = symbol_factory.BitVecVal(int(address, 16), 256) + if isinstance(address, int): + address = symbol_factory.BitVecVal(address, 256) + if strategy == "dfs": - s_strategy = DepthFirstSearchStrategy + s_strategy = DepthFirstSearchStrategy # type: Type[BasicSearchStrategy] elif strategy == "bfs": s_strategy = BreadthFirstSearchStrategy elif strategy == "naive-random": @@ -66,19 +71,11 @@ class SymExecWrapper: else: raise ValueError("Invalid strategy argument supplied") - account = Account( - address, - contract.disassembly, - dynamic_loader=dynloader, - contract_name=contract.name, - ) requires_statespace = ( compulsory_statespace or len(get_detection_modules("post", modules)) > 0 ) - self.accounts = {address: account} self.laser = svm.LaserEVM( - self.accounts, dynamic_loader=dynloader, max_depth=max_depth, execution_timeout=execution_timeout, @@ -111,12 +108,16 @@ class SymExecWrapper: creation_code=contract.creation_code, contract_name=contract.name ) else: - self.laser.sym_exec(address) - created_address = "0x" + str(mk_contract_address(CREATOR_ADDRESS, 0).hex()) - for key, value in self.laser.world_state.accounts.items(): - if created_address == value.address: - contract.code = value.code.bytecode - break + account = Account( + address, + contract.disassembly, + dynamic_loader=dynloader, + contract_name=contract.name, + concrete_storage=False, + ) + world_state = WorldState() + world_state.put_account(account) + self.laser.sym_exec(world_state=world_state, target_address=address.value) if not requires_statespace: return @@ -126,8 +127,8 @@ class SymExecWrapper: # Generate lists of interesting operations - self.calls = [] - self.sstors = {} + self.calls = [] # type: List[Call] + self.sstors = {} # type: Dict[int, Dict[str, List[SStore]]] for key in self.nodes: @@ -204,7 +205,7 @@ class SymExecWrapper: elif op == "SSTORE": stack = copy.copy(state.mstate.stack) - address = state.environment.active_account.address + address = state.environment.active_account.address.value index, value = stack.pop(), stack.pop() diff --git a/mythril/laser/ethereum/call.py b/mythril/laser/ethereum/call.py index da63928d..08ef8773 100644 --- a/mythril/laser/ethereum/call.py +++ b/mythril/laser/ethereum/call.py @@ -3,13 +3,12 @@ instructions.py to get the necessary elements from the stack and determine the parameters for the new global state.""" import logging +import re from typing import Union, List, cast, Callable -from z3 import Z3Exception -from mythril.laser.smt import BitVec + +import mythril.laser.ethereum.util as util from mythril.laser.ethereum import natives from mythril.laser.ethereum.gas import OPCODE_GAS -from mythril.laser.smt import simplify, Expression, symbol_factory -import mythril.laser.ethereum.util as util from mythril.laser.ethereum.state.account import Account from mythril.laser.ethereum.state.calldata import ( BaseCalldata, @@ -17,8 +16,9 @@ from mythril.laser.ethereum.state.calldata import ( ConcreteCalldata, ) from mythril.laser.ethereum.state.global_state import GlobalState +from mythril.laser.smt import BitVec +from mythril.laser.smt import simplify, Expression, symbol_factory from mythril.support.loader import DynLoader -import re """ This module contains the business logic used by Instruction in instructions.py @@ -96,7 +96,7 @@ def get_callee_address( # attempt to read the contract address from instance storage try: callee_address = dynamic_loader.read_storage( - environment.active_account.address, index + str(hex(environment.active_account.address.value)), index ) # TODO: verify whether this happens or not except: @@ -124,7 +124,7 @@ def get_callee_account( accounts = global_state.accounts try: - return global_state.accounts[callee_address] + return global_state.accounts[int(callee_address, 16)] except KeyError: # We have a valid call address, but contract is not in the modules list log.debug("Module with address " + callee_address + " not loaded.") @@ -145,7 +145,11 @@ def get_callee_account( log.debug("Dependency loaded: " + callee_address) callee_account = Account( - callee_address, code, callee_address, dynamic_loader=dynamic_loader + symbol_factory.BitVecVal(int(callee_address, 16), 256), + code, + callee_address, + dynamic_loader=dynamic_loader, + balances=global_state.world_state.balances, ) accounts[callee_address] = callee_account diff --git a/mythril/laser/ethereum/instructions.py b/mythril/laser/ethereum/instructions.py index b6bb61cd..a353fbc1 100644 --- a/mythril/laser/ethereum/instructions.py +++ b/mythril/laser/ethereum/instructions.py @@ -1520,7 +1520,7 @@ class Instruction: global_state.environment.active_account ) global_state.accounts[ - global_state.environment.active_account.address + global_state.environment.active_account.address.value ] = global_state.environment.active_account global_state.environment.active_account.storage[index] = ( @@ -1756,31 +1756,19 @@ class Instruction: :param global_state: """ target = global_state.mstate.stack.pop() - account_created = False + transfer_amount = global_state.environment.active_account.balance() # Often the target of the suicide instruction will be symbolic - # If it isn't then well transfer the balance to the indicated contract - if isinstance(target, BitVec) and not target.symbolic: - target = "0x" + hex(target.value)[-40:] - if isinstance(target, str): - try: - global_state.world_state[ - target - ].balance += global_state.environment.active_account.balance - except KeyError: - global_state.world_state.create_account( - address=target, - balance=global_state.environment.active_account.balance, - ) - account_created = True + # If it isn't then we'll transfer the balance to the indicated contract + global_state.world_state[target].add_balance(transfer_amount) global_state.environment.active_account = deepcopy( global_state.environment.active_account ) global_state.accounts[ - global_state.environment.active_account.address + global_state.environment.active_account.address.value ] = global_state.environment.active_account - global_state.environment.active_account.balance = 0 + global_state.environment.active_account.set_balance(0) global_state.environment.active_account.deleted = True global_state.current_transaction.end(global_state) @@ -1865,9 +1853,7 @@ class Instruction: gas_price=environment.gasprice, gas_limit=gas, origin=environment.origin, - caller=symbol_factory.BitVecVal( - int(environment.active_account.address, 16), 256 - ), + caller=environment.active_account.address, callee_account=callee_account, call_data=call_data, call_value=value, diff --git a/mythril/laser/ethereum/state/account.py b/mythril/laser/ethereum/state/account.py index e608246e..381562ca 100644 --- a/mythril/laser/ethereum/state/account.py +++ b/mythril/laser/ethereum/state/account.py @@ -7,6 +7,7 @@ from typing import Any, Dict, KeysView, Union from z3 import ExprRef +from mythril.laser.smt import Array, symbol_factory, BitVec from mythril.disassembler.disassembly import Disassembly from mythril.laser.smt import symbol_factory @@ -30,7 +31,7 @@ class Storage: except KeyError: if ( self.address - and int(self.address[2:], 16) != 0 + and self.address.value != 0 and (self.dynld and self.dynld.storage_loading) ): try: @@ -74,10 +75,10 @@ class Account: def __init__( self, - address: str, + address: Union[BitVec, str], code=None, contract_name="unknown", - balance=None, + balances: Array = None, concrete_storage=False, dynamic_loader=None, ) -> None: @@ -91,36 +92,51 @@ class Account: """ self.nonce = 0 self.code = code or Disassembly("") - self.balance = ( - balance - if balance - else symbol_factory.BitVecSym("{}_balance".format(address), 256) + self.address = ( + address + if isinstance(address, BitVec) + else symbol_factory.BitVecVal(int(address, 16), 256) ) + self.storage = Storage( - concrete_storage, address=address, dynamic_loader=dynamic_loader + concrete_storage, address=self.address, dynamic_loader=dynamic_loader ) + # Metadata - self.address = address self.contract_name = contract_name self.deleted = False + self._balances = balances + self.balance = lambda: self._balances[self.address] + def __str__(self) -> str: return str(self.as_dict) - def set_balance(self, balance: ExprRef) -> None: + def set_balance(self, balance: Union[int, BitVec]) -> None: """ :param balance: """ - self.balance = balance + balance = ( + symbol_factory.BitVecVal(balance, 256) + if isinstance(balance, int) + else balance + ) + assert self._balances is not None + self._balances[self.address] = balance - def add_balance(self, balance: ExprRef) -> None: + def add_balance(self, balance: Union[int, BitVec]) -> None: """ :param balance: """ - self.balance += balance + balance = ( + symbol_factory.BitVecVal(balance, 256) + if isinstance(balance, int) + else balance + ) + self._balances[self.address] += balance @property def as_dict(self) -> Dict: @@ -131,7 +147,7 @@ class Account: return { "nonce": self.nonce, "code": self.code, - "balance": self.balance, + "balance": self.balance(), "storage": self.storage, } @@ -139,8 +155,8 @@ class Account: new_account = Account( address=self.address, code=self.code, - balance=self.balance, contract_name=self.contract_name, + balances=self._balances, ) new_account.storage = deepcopy(self.storage) new_account.code = self.code diff --git a/mythril/laser/ethereum/state/environment.py b/mythril/laser/ethereum/state/environment.py index 69830007..0f467413 100644 --- a/mythril/laser/ethereum/state/environment.py +++ b/mythril/laser/ethereum/state/environment.py @@ -39,7 +39,7 @@ class Environment: self.active_account = active_account self.active_function_name = "" - self.address = symbol_factory.BitVecVal(int(active_account.address, 16), 256) + self.address = active_account.address # Ib self.code = active_account.code if code is None else code diff --git a/mythril/laser/ethereum/state/global_state.py b/mythril/laser/ethereum/state/global_state.py index 5423aaf8..e6a13cd3 100644 --- a/mythril/laser/ethereum/state/global_state.py +++ b/mythril/laser/ethereum/state/global_state.py @@ -77,7 +77,7 @@ class GlobalState: :return: """ - return self.world_state.accounts + return self.world_state._accounts # TODO: remove this, as two instructions are confusing def get_current_instruction(self) -> Dict: diff --git a/mythril/laser/ethereum/state/world_state.py b/mythril/laser/ethereum/state/world_state.py index 862b7c8d..ba8611d0 100644 --- a/mythril/laser/ethereum/state/world_state.py +++ b/mythril/laser/ethereum/state/world_state.py @@ -2,6 +2,8 @@ from copy import copy from random import randint from typing import Dict, List, Iterator, Optional, TYPE_CHECKING + +from mythril.laser.smt import symbol_factory, Array, BitVec from ethereum.utils import mk_contract_address from mythril.laser.ethereum.state.account import Account from mythril.laser.ethereum.state.annotation import StateAnnotation @@ -22,18 +24,29 @@ class WorldState: :param transaction_sequence: :param annotations: """ - self.accounts = {} # type: Dict[str, Account] + self._accounts = {} # type: Dict[int, Account] + self.balances = Array("balance", 256, 256) + self.node = None # type: Optional['Node'] self.transaction_sequence = transaction_sequence or [] self._annotations = annotations or [] - def __getitem__(self, item: str) -> Account: + @property + def accounts(self): + return self._accounts + + def __getitem__(self, item: BitVec) -> 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] + try: + return self._accounts[item.value] + except KeyError: + new_account = Account(address=item, code=None, balances=self.balances) + self._accounts[item.value] = new_account + return new_account def __copy__(self) -> "WorldState": """ @@ -45,7 +58,9 @@ class WorldState: transaction_sequence=self.transaction_sequence[:], annotations=new_annotations, ) - new_world_state.accounts = copy(self.accounts) + new_world_state.balances = copy(self.balances) + for account in self._accounts.values(): + new_world_state.put_account(copy(account)) new_world_state.node = self.node return new_world_state @@ -65,14 +80,22 @@ class WorldState: :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(creator) + address = ( + symbol_factory.BitVecVal(address, 256) + if address + else self._generate_new_address(creator) + ) + new_account = Account( - address, - balance=balance, + address=address, + balances=self.balances, dynamic_loader=dynamic_loader, concrete_storage=concrete_storage, ) - self._put_account(new_account) + if balance: + new_account.set_balance(symbol_factory.BitVecVal(balance, 256)) + + self.put_account(new_account) return new_account def create_initialized_contract_account(self, contract_code, storage) -> None: @@ -86,10 +109,10 @@ class WorldState: """ # TODO: Add type hints new_account = Account( - self._generate_new_address(), code=contract_code, balance=0 + self._generate_new_address(), code=contract_code, balances=self.balances ) new_account.storage = storage - self._put_account(new_account) + self.put_account(new_account) def annotate(self, annotation: StateAnnotation) -> None: """ @@ -116,22 +139,24 @@ class WorldState: """ return filter(lambda x: isinstance(x, annotation_type), self.annotations) - def _generate_new_address(self, creator=None) -> str: + def _generate_new_address(self, creator=None) -> BitVec: """Generates a new address for the global state. :return: """ if creator: # TODO: Use nounce - return "0x" + str(mk_contract_address(creator, 0).hex()) + address = "0x" + str(mk_contract_address(creator, 0).hex()) + return symbol_factory.BitVecVal(int(address, 16), 256) while True: address = "0x" + "".join([str(hex(randint(0, 16)))[-1] for _ in range(40)]) - if address not in self.accounts.keys(): - return address + if address not in self._accounts.keys(): + return symbol_factory.BitVecVal(int(address, 16), 256) - def _put_account(self, account: Account) -> None: + def put_account(self, account: Account) -> None: """ :param account: """ - self.accounts[account.address] = account + self._accounts[account.address.value] = account + account._balances = self.balances diff --git a/mythril/laser/ethereum/svm.py b/mythril/laser/ethereum/svm.py index 0fcb8d77..fae71dc8 100644 --- a/mythril/laser/ethereum/svm.py +++ b/mythril/laser/ethereum/svm.py @@ -9,12 +9,12 @@ from mythril.laser.ethereum.cfg import NodeFlags, Node, Edge, JumpType from mythril.laser.ethereum.evm_exceptions import StackUnderflowException from mythril.laser.ethereum.evm_exceptions import VmException from mythril.laser.ethereum.instructions import Instruction -from mythril.laser.ethereum.state.account import Account +from mythril.laser.ethereum.iprof import InstructionProfiler +from mythril.laser.ethereum.plugins.signals import PluginSkipWorldState from mythril.laser.ethereum.state.global_state import GlobalState from mythril.laser.ethereum.state.world_state import WorldState from mythril.laser.ethereum.strategy.basic import DepthFirstSearchStrategy from mythril.laser.ethereum.time_handler import time_handler -from mythril.laser.ethereum.plugins.signals import PluginSkipWorldState from mythril.laser.ethereum.transaction import ( ContractCreationTransaction, TransactionEndSignal, @@ -22,7 +22,7 @@ from mythril.laser.ethereum.transaction import ( execute_contract_creation, execute_message_call, ) -from mythril.laser.ethereum.iprof import InstructionProfiler +from mythril.laser.smt import symbol_factory log = logging.getLogger(__name__) @@ -46,7 +46,6 @@ class LaserEVM: def __init__( self, - accounts: Dict[str, Account], dynamic_loader=None, max_depth=float("inf"), execution_timeout=60, @@ -57,21 +56,18 @@ class LaserEVM: enable_iprof=False, ) -> None: """ - - :param accounts: - :param dynamic_loader: - :param max_depth: - :param execution_timeout: - :param create_timeout: - :param strategy: - :param transaction_count: + Initializes the laser evm object + + :param dynamic_loader: Loads data from chain + :param max_depth: Maximum execution depth this vm should execute + :param execution_timeout: Time to take for execution + :param create_timeout: Time to take for contract creation + :param strategy: Execution search strategy + :param transaction_count: The amount of transactions to execute + :param requires_statespace: Variable indicating whether the statespace should be recorded + :param enable_iprof: Variable indicating whether instruction profiling should be turned on """ - world_state = WorldState() - world_state.accounts = accounts - # this sets the initial world state - self.world_state = world_state - self.open_states = [world_state] - + self.open_states = [] # type: List[WorldState] self.total_states = 0 self.dynamic_loader = dynamic_loader @@ -106,23 +102,30 @@ class LaserEVM: log.info("LASER EVM initialized with dynamic loader: " + str(dynamic_loader)) - @property - def accounts(self) -> Dict[str, Account]: - """ - - :return: - """ - return self.world_state.accounts - def sym_exec( - self, main_address=None, creation_code=None, contract_name=None + self, + world_state: WorldState = None, + target_address: int = None, + creation_code: str = None, + contract_name: str = None, ) -> None: + """ Starts symbolic execution + There are two modes of execution. + Either we analyze a preconfigured configuration, in which case the world_state and target_address variables + must be supplied. + Or we execute the creation code of a contract, in which case the creation code and desired name of that + contract should be provided. + + :param world_state The world state configuration from which to perform analysis + :param target_address The address of the contract account in the world state which analysis should target + :param creation_code The creation code to create the target contract in the symbolic environment + :param contract_name The name that the created account should be associated with """ + pre_configuration_mode = world_state is not None and target_address is not None + scratch_mode = creation_code is not None and contract_name is not None + if pre_configuration_mode == scratch_mode: + raise ValueError("Symbolic execution started with invalid parameters") - :param main_address: - :param creation_code: - :param contract_name: - """ log.debug("Starting LASER execution") for hook in self._start_sym_exec_hooks: hook() @@ -130,11 +133,12 @@ class LaserEVM: time_handler.start_execution(self.execution_timeout) self.time = datetime.now() - if main_address: - log.info("Starting message call transaction to {}".format(main_address)) - self._execute_transactions(main_address) + if pre_configuration_mode: + self.open_states = [world_state] + log.info("Starting message call transaction to {}".format(target_address)) + self._execute_transactions(symbol_factory.BitVecVal(target_address, 256)) - elif creation_code: + elif scratch_mode: log.info("Starting contract creation transaction") created_account = execute_contract_creation( self, creation_code, contract_name @@ -355,7 +359,7 @@ class LaserEVM: if not revert_changes: return_global_state.world_state = copy(global_state.world_state) return_global_state.environment.active_account = global_state.accounts[ - return_global_state.environment.active_account.address + return_global_state.environment.active_account.address.value ] # Execute the post instruction handler @@ -389,19 +393,6 @@ class LaserEVM: self._new_node_state( state, JumpType.CONDITIONAL, state.mstate.constraints[-1] ) - - elif opcode in ("CALL", "CALLCODE", "DELEGATECALL", "STATICCALL"): - assert len(new_states) <= 1 - for state in new_states: - self._new_node_state(state, JumpType.CALL) - # Keep track of added contracts so the graph can be generated properly - if ( - state.environment.active_account.contract_name - not in self.world_state.accounts.keys() - ): - self.world_state.accounts[ - state.environment.active_account.address - ] = state.environment.active_account elif opcode == "RETURN": for state in new_states: self._new_node_state(state, JumpType.RETURN) diff --git a/mythril/laser/ethereum/transaction/symbolic.py b/mythril/laser/ethereum/transaction/symbolic.py index def5048e..9a8ccc5d 100644 --- a/mythril/laser/ethereum/transaction/symbolic.py +++ b/mythril/laser/ethereum/transaction/symbolic.py @@ -2,25 +2,32 @@ symbolic values.""" import logging - -from mythril.laser.smt import symbol_factory from mythril.disassembler.disassembly import Disassembly from mythril.laser.ethereum.cfg import Node, Edge, JumpType -from mythril.laser.ethereum.state.calldata import BaseCalldata, SymbolicCalldata from mythril.laser.ethereum.state.account import Account +from mythril.laser.ethereum.state.calldata import SymbolicCalldata +from mythril.laser.ethereum.state.world_state import WorldState from mythril.laser.ethereum.transaction.transaction_models import ( MessageCallTransaction, ContractCreationTransaction, get_next_transaction_id, + BaseTransaction, ) +from mythril.laser.smt import symbol_factory, Or, BitVec log = logging.getLogger(__name__) CREATOR_ADDRESS = 0xAFFEAFFEAFFEAFFEAFFEAFFEAFFEAFFEAFFEAFFE ATTACKER_ADDRESS = 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF +ACTOR_ADDRESSES = [ + symbol_factory.BitVecVal(0xAFFEAFFEAFFEAFFEAFFEAFFEAFFEAFFEAFFEAFFE, 256), + symbol_factory.BitVecVal(0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF, 256), + symbol_factory.BitVecVal(0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEE, 256), +] + -def execute_message_call(laser_evm, callee_address: str) -> None: +def execute_message_call(laser_evm, callee_address: BitVec) -> None: """Executes a message call transaction from all open states. :param laser_evm: @@ -46,7 +53,9 @@ def execute_message_call(laser_evm, callee_address: str) -> None: origin=symbol_factory.BitVecSym( "origin{}".format(next_transaction_id), 256 ), - caller=symbol_factory.BitVecVal(ATTACKER_ADDRESS, 256), + caller=symbol_factory.BitVecSym( + "sender_{}".format(next_transaction_id), 256 + ), callee_account=open_world_state[callee_address], call_data=SymbolicCalldata(next_transaction_id), call_value=symbol_factory.BitVecSym( @@ -69,12 +78,14 @@ def execute_contract_creation( :return: """ # TODO: Resolve circular import between .transaction and ..svm to import LaserEVM here - open_states = laser_evm.open_states[:] del laser_evm.open_states[:] - new_account = laser_evm.world_state.create_account( + world_state = WorldState() + open_states = [world_state] + new_account = world_state.create_account( 0, concrete_storage=True, dynamic_loader=None, creator=CREATOR_ADDRESS ) + if contract_name: new_account.contract_name = contract_name @@ -104,7 +115,7 @@ def execute_contract_creation( return new_account -def _setup_global_state_for_execution(laser_evm, transaction) -> None: +def _setup_global_state_for_execution(laser_evm, transaction: BaseTransaction) -> None: """Sets up global state and cfg for a transactions execution. :param laser_evm: @@ -114,6 +125,10 @@ def _setup_global_state_for_execution(laser_evm, transaction) -> None: global_state = transaction.initial_global_state() global_state.transaction_stack.append((transaction, None)) + global_state.mstate.constraints.append( + Or(*[transaction.caller == actor for actor in ACTOR_ADDRESSES]) + ) + new_node = Node( global_state.environment.active_account.contract_name, function_name=global_state.environment.active_function_name, diff --git a/mythril/laser/ethereum/transaction/transaction_models.py b/mythril/laser/ethereum/transaction/transaction_models.py index 3c5aabf4..f5863c1c 100644 --- a/mythril/laser/ethereum/transaction/transaction_models.py +++ b/mythril/laser/ethereum/transaction/transaction_models.py @@ -111,8 +111,19 @@ class BaseTransaction: # Initialize the execution environment global_state = GlobalState(self.world_state, environment, None) global_state.environment.active_function_name = active_function + + sender = environment.sender + receiver = environment.active_account.address + value = environment.callvalue + + global_state.world_state.balances[sender] -= value + global_state.world_state.balances[receiver] += value + return global_state + def initial_global_state(self) -> GlobalState: + raise NotImplementedError + class MessageCallTransaction(BaseTransaction): """Transaction object models an transaction.""" @@ -143,6 +154,7 @@ class MessageCallTransaction(BaseTransaction): :param revert: """ self.return_data = return_data + raise TransactionEndSignal(global_state, revert) @@ -190,7 +202,9 @@ class ContractCreationTransaction(BaseTransaction): contract_code = bytes.hex(array.array("B", return_data).tostring()) global_state.environment.active_account.code.assign_bytecode(contract_code) - self.return_data = global_state.environment.active_account.address + self.return_data = str( + hex(global_state.environment.active_account.address.value) + ) assert global_state.environment.active_account.code.instruction_list != [] raise TransactionEndSignal(global_state, revert=revert) diff --git a/mythril/laser/smt/bool.py b/mythril/laser/smt/bool.py index 9fa097e4..1dd1cc76 100644 --- a/mythril/laser/smt/bool.py +++ b/mythril/laser/smt/bool.py @@ -90,15 +90,16 @@ def And(*args: Union[Bool, bool]) -> Bool: return Bool(z3.And([a.raw for a in args_list]), union) -def Or(a: Bool, b: Bool) -> Bool: +def Or(*args: Union[Bool, bool]) -> Bool: """Create an or expression. :param a: :param b: :return: """ - union = a.annotations + b.annotations - return Bool(z3.Or(a.raw, b.raw), annotations=union) + args_list = [arg if isinstance(arg, Bool) else Bool(arg) for arg in args] + union = [arg.annotations for arg in args_list] + return Bool(z3.Or([a.raw for a in args_list]), annotations=union) def Not(a: Bool) -> Bool: diff --git a/tests/laser/evm_testsuite/evm_test.py b/tests/laser/evm_testsuite/evm_test.py index 6227b3ce..22a48070 100644 --- a/tests/laser/evm_testsuite/evm_test.py +++ b/tests/laser/evm_testsuite/evm_test.py @@ -1,8 +1,9 @@ from mythril.laser.ethereum.svm import LaserEVM from mythril.laser.ethereum.state.account import Account +from mythril.laser.ethereum.state.world_state import WorldState from mythril.disassembler.disassembly import Disassembly from mythril.laser.ethereum.transaction.concolic import execute_message_call -from mythril.laser.smt import Expression, BitVec +from mythril.laser.smt import Expression, BitVec, symbol_factory from mythril.analysis.solver import get_model from datetime import datetime @@ -82,25 +83,27 @@ def test_vmtest( # Arrange if test_name == "gasprice": return - accounts = {} + + world_state = WorldState() + for address, details in pre_condition.items(): account = Account(address) account.code = Disassembly(details["code"][2:]) - account.balance = int(details["balance"], 16) account.nonce = int(details["nonce"], 16) + world_state.put_account(account) + account.set_balance(int(details["balance"], 16)) - accounts[address] = account - - laser_evm = LaserEVM(accounts) + laser_evm = LaserEVM() + laser_evm.open_states = [world_state] # Act laser_evm.time = datetime.now() final_states = execute_message_call( laser_evm, - callee_address=action["address"], - caller_address=action["caller"], - origin_address=binascii.a2b_hex(action["origin"][2:]), + callee_address=symbol_factory.BitVecVal(int(action["address"], 16), 256), + caller_address=symbol_factory.BitVecVal(int(action["caller"], 16), 256), + origin_address=symbol_factory.BitVecVal(int(action["origin"], 16), 256), code=action["code"][2:], gas_limit=int(action["gas"], 16), data=binascii.a2b_hex(action["data"][2:]), @@ -132,7 +135,7 @@ def test_vmtest( ) for address, details in post_condition.items(): - account = world_state[address] + account = world_state[symbol_factory.BitVecVal(int(address, 16), 256)] assert account.nonce == int(details["nonce"], 16) assert account.code.bytecode == details["code"][2:] diff --git a/tests/laser/test_transaction.py b/tests/laser/test_transaction.py index 8e9ac415..b741ab66 100644 --- a/tests/laser/test_transaction.py +++ b/tests/laser/test_transaction.py @@ -1,6 +1,8 @@ from mythril.disassembler.disassembly import Disassembly from mythril.laser.ethereum import svm from mythril.laser.ethereum.state.account import Account +from mythril.laser.ethereum.state.world_state import WorldState + import mythril.laser.ethereum.cfg as cfg @@ -18,15 +20,17 @@ def test_intercontract_call(): ) callee_address = "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef" + world_state = WorldState() + caller_account = Account(caller_address, caller_code, contract_name="Caller") callee_account = Account(callee_address, callee_code, contract_name="Callee") + world_state.put_account(callee_account) + world_state.put_account(caller_account) - accounts = {caller_address: caller_account, callee_address: callee_account} - - laser = svm.LaserEVM(accounts) + laser = svm.LaserEVM() # Act - laser.sym_exec(caller_address) + laser.sym_exec(world_state=world_state, target_address=int(caller_address, 16)) # Assert # Initial node starts in contract caller diff --git a/tests/laser/transaction/symbolic_test.py b/tests/laser/transaction/symbolic_test.py index f3939528..3ff46917 100644 --- a/tests/laser/transaction/symbolic_test.py +++ b/tests/laser/transaction/symbolic_test.py @@ -9,6 +9,8 @@ from mythril.laser.ethereum.transaction import ( from mythril.laser.ethereum.svm import LaserEVM from mythril.laser.ethereum.state.account import Account from mythril.laser.ethereum.state.world_state import WorldState +from mythril.laser.smt import symbol_factory + import unittest.mock as mock from unittest.mock import MagicMock @@ -29,7 +31,7 @@ def test_execute_message_call(mocked_setup: MagicMock): laser_evm = LaserEVM({}) world_state = WorldState() - world_state.accounts["address"] = Account("address") + world_state.put_account(Account("0x0")) laser_evm.open_states = [world_state] laser_evm.exec = MagicMock() @@ -37,7 +39,7 @@ def test_execute_message_call(mocked_setup: MagicMock): mocked_setup.side_effect = _is_message_call # Act - execute_message_call(laser_evm, "address") + execute_message_call(laser_evm, symbol_factory.BitVecVal(0, 256)) # Assert # laser_evm.exec.assert_called_once() diff --git a/tests/native_test.py b/tests/native_test.py index c58a25e3..b9fd3671 100644 --- a/tests/native_test.py +++ b/tests/native_test.py @@ -3,6 +3,7 @@ from mythril.mythril import MythrilDisassembler from mythril.laser.ethereum.state.account import Account from mythril.laser.ethereum.state.machine_state import MachineState from mythril.laser.ethereum.state.global_state import GlobalState +from mythril.laser.ethereum.state.world_state import WorldState from mythril.laser.ethereum import svm from tests import BaseTestCase @@ -29,13 +30,6 @@ IDENTITY_TEST[1] = (hex(476934798798347), False) def _all_info(laser): - accounts = {} - for address, _account in laser.world_state.accounts.items(): - account = _account.as_dict - account["code"] = account["code"].instruction_list - account["balance"] = str(account["balance"]) - accounts[address] = account - nodes = {} for uid, node in laser.nodes.items(): states = [] @@ -66,7 +60,6 @@ def _all_info(laser): edges = [edge.as_dict for edge in laser.edges] return { - "accounts": accounts, "nodes": nodes, "edges": edges, "total_states": laser.total_states, @@ -85,13 +78,13 @@ class NativeTests(BaseTestCase): """""" disassembly = SolidityContract( "./tests/native_tests.sol", - solc_binary=MythrilDisassembler._init_solc_binary("0.5.0"), + solc_binary=MythrilDisassembler._init_solc_binary("0.5.3"), ).disassembly account = Account("0x0000000000000000000000000000000000000000", disassembly) - accounts = {account.address: account} - - laser = svm.LaserEVM(accounts, max_depth=100, transaction_count=1) - laser.sym_exec(account.address) + world_state = WorldState() + world_state.put_account(account) + laser = svm.LaserEVM(max_depth=100, transaction_count=1) + laser.sym_exec(world_state=world_state, target_address=account.address.value) laser_info = str(_all_info(laser)) diff --git a/tests/report_test.py b/tests/report_test.py index 30e9cabd..e83f3d67 100644 --- a/tests/report_test.py +++ b/tests/report_test.py @@ -32,7 +32,7 @@ def _generate_report(input_file): contract = EVMContract(input_file.read_text(), enable_online_lookup=False) sym = SymExecWrapper( contract, - address=(util.get_indexed_address(0)), + address=0xAFFEAFFEAFFEAFFEAFFEAFFEAFFEAFFEAFFEAFFE, strategy="dfs", execution_timeout=30, transaction_count=1, diff --git a/tests/testdata/outputs_expected/calls.sol.o.json b/tests/testdata/outputs_expected/calls.sol.o.json index 624cf510..fbcd2784 100644 --- a/tests/testdata/outputs_expected/calls.sol.o.json +++ b/tests/testdata/outputs_expected/calls.sol.o.json @@ -1,123 +1,123 @@ { - "error":null, - "issues":[ - { - "address":661, - "contract":"Unknown", - "debug":"", - "description":"The contract executes an external message call.\nAn external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully.", - "function":"thisisfine()", - "max_gas_used":1254, - "min_gas_used":643, - "severity":"Low", - "sourceMap":null, - "swc-id":"107", - "title":"External Call To Fixed Address" - }, - { - "address":661, - "contract":"Unknown", - "debug":"", - "description":"The return value of a message call is not checked.\nExternal calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states.", - "function":"thisisfine()", - "max_gas_used":35972, - "min_gas_used":1361, - "severity":"Low", - "sourceMap":null, - "swc-id":"104", - "title":"Unchecked Call Return Value" - }, - { - "address":779, - "contract":"Unknown", - "debug":"", - "description":"The contract executes an external message call.\nAn external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully.", - "function":"callstoredaddress()", - "max_gas_used":1298, - "min_gas_used":687, - "severity":"Low", - "sourceMap":null, - "swc-id":"107", - "title":"External Call To Fixed Address" - }, - { - "address":779, - "contract":"Unknown", - "debug":"", - "description":"The return value of a message call is not checked.\nExternal calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states.", - "function":"callstoredaddress()", - "max_gas_used":36016, - "min_gas_used":1405, - "severity":"Low", - "sourceMap":null, - "swc-id":"104", - "title":"Unchecked Call Return Value" - }, - { - "address":858, - "contract":"Unknown", - "debug":"", - "description":"The contract executes an external message call.\nAn external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully.", - "function":"reentrancy()", - "max_gas_used":1320, - "min_gas_used":709, - "severity":"Low", - "sourceMap":null, - "swc-id":"107", - "title":"External Call To Fixed Address" - }, - { - "address":858, - "contract":"Unknown", - "debug":"", - "description":"The return value of a message call is not checked.\nExternal calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states.", - "function":"reentrancy()", - "max_gas_used":61052, - "min_gas_used":6441, - "severity":"Low", - "sourceMap":null, - "swc-id":"104", - "title":"Unchecked Call Return Value" - }, - { - "address":869, - "contract":"Unknown", - "debug":"", - "description":"The contract account state is changed after an external call. \nConsider that the called contract could re-enter the function before this state change takes place. This can lead to business logic vulnerabilities.", - "function":"reentrancy()", - "max_gas_used":null, - "min_gas_used":null, - "severity":"Low", - "sourceMap":null, - "swc-id":"107", - "title":"State change after external call" - }, - { - "address":912, - "contract":"Unknown", - "debug":"", - "description":"A call to a user-supplied address is executed.\nThe callee address of an external message call can be set by the caller. Note that the callee can contain arbitrary code and may re-enter any function in this contract. Review the business logic carefully to prevent averse effects on the contract state.", - "function":"calluseraddress(address)", - "max_gas_used":616, - "min_gas_used":335, - "severity":"Medium", - "sourceMap":null, - "swc-id":"107", - "title":"External Call To User-Supplied Address" - }, - { - "address":912, - "contract":"Unknown", - "debug":"", - "description":"The return value of a message call is not checked.\nExternal calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states.", - "function":"calluseraddress(address)", - "max_gas_used":35336, - "min_gas_used":1055, - "severity":"Low", - "sourceMap":null, - "swc-id":"104", - "title":"Unchecked Call Return Value" - } - ], - "success":true -} + "error": null, + "issues": [ + { + "address": 661, + "contract": "Unknown", + "debug": "", + "description": "The contract executes an external message call.\nAn external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully.", + "function": "thisisfine()", + "max_gas_used": 1254, + "min_gas_used": 643, + "severity": "Low", + "sourceMap": null, + "swc-id": "107", + "title": "External Call To Fixed Address" + }, + { + "address": 661, + "contract": "Unknown", + "debug": "", + "description": "The return value of a message call is not checked.\nExternal calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states.", + "function": "thisisfine()", + "max_gas_used": 35972, + "min_gas_used": 1361, + "severity": "Low", + "sourceMap": null, + "swc-id": "104", + "title": "Unchecked Call Return Value" + }, + { + "address": 779, + "contract": "Unknown", + "debug": "", + "description": "The contract executes an external message call.\nAn external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully.", + "function": "callstoredaddress()", + "max_gas_used": 1298, + "min_gas_used": 687, + "severity": "Low", + "sourceMap": null, + "swc-id": "107", + "title": "External Call To Fixed Address" + }, + { + "address": 779, + "contract": "Unknown", + "debug": "", + "description": "The return value of a message call is not checked.\nExternal calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states.", + "function": "callstoredaddress()", + "max_gas_used": 36016, + "min_gas_used": 1405, + "severity": "Low", + "sourceMap": null, + "swc-id": "104", + "title": "Unchecked Call Return Value" + }, + { + "address": 858, + "contract": "Unknown", + "debug": "", + "description": "The contract executes an external message call.\nAn external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully.", + "function": "reentrancy()", + "max_gas_used": 1320, + "min_gas_used": 709, + "severity": "Low", + "sourceMap": null, + "swc-id": "107", + "title": "External Call To Fixed Address" + }, + { + "address": 858, + "contract": "Unknown", + "debug": "", + "description": "The return value of a message call is not checked.\nExternal calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states.", + "function": "reentrancy()", + "max_gas_used": 61052, + "min_gas_used": 6441, + "severity": "Low", + "sourceMap": null, + "swc-id": "104", + "title": "Unchecked Call Return Value" + }, + { + "address": 869, + "contract": "Unknown", + "debug": "", + "description": "The contract account state is changed after an external call. \nConsider that the called contract could re-enter the function before this state change takes place. This can lead to business logic vulnerabilities.", + "function": "reentrancy()", + "max_gas_used": null, + "min_gas_used": null, + "severity": "Low", + "sourceMap": null, + "swc-id": "107", + "title": "State change after external call" + }, + { + "address": 912, + "contract": "Unknown", + "debug": "", + "description": "A call to a user-supplied address is executed.\nThe callee address of an external message call can be set by the caller. Note that the callee can contain arbitrary code and may re-enter any function in this contract. Review the business logic carefully to prevent averse effects on the contract state.", + "function": "calluseraddress(address)", + "max_gas_used": 616, + "min_gas_used": 335, + "severity": "Medium", + "sourceMap": null, + "swc-id": "107", + "title": "External Call To User-Supplied Address" + }, + { + "address": 912, + "contract": "Unknown", + "debug": "", + "description": "The return value of a message call is not checked.\nExternal calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states.", + "function": "calluseraddress(address)", + "max_gas_used": 35336, + "min_gas_used": 1055, + "severity": "Low", + "sourceMap": null, + "swc-id": "104", + "title": "Unchecked Call Return Value" + } + ], + "success": true +} \ No newline at end of file diff --git a/tests/testdata/outputs_expected/calls.sol.o.jsonv2 b/tests/testdata/outputs_expected/calls.sol.o.jsonv2 index 44d5bc89..624d3410 100644 --- a/tests/testdata/outputs_expected/calls.sol.o.jsonv2 +++ b/tests/testdata/outputs_expected/calls.sol.o.jsonv2 @@ -162,4 +162,4 @@ ], "sourceType": "raw-bytecode" } -] +] \ No newline at end of file diff --git a/tests/testdata/outputs_expected/environments.sol.o.json b/tests/testdata/outputs_expected/environments.sol.o.json deleted file mode 100644 index d4ae2a37..00000000 --- a/tests/testdata/outputs_expected/environments.sol.o.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "error": null, - "issues": [ - { - "address": 158, - "contract": "Unknown", - "debug": "", - "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": "", - "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": "", - "description": "The substraction can result in an integer underflow.\n", - "function": "_function_0x83f12fec", - "swc-id": "101", - "title": "Integer Underflow", - "type": "Warning" - } - ], - "success": true -} \ No newline at end of file diff --git a/tests/testdata/outputs_expected/environments.sol.o.markdown b/tests/testdata/outputs_expected/environments.sol.o.markdown deleted file mode 100644 index c9630745..00000000 --- a/tests/testdata/outputs_expected/environments.sol.o.markdown +++ /dev/null @@ -1,34 +0,0 @@ -# Analysis results for test-filename.sol - -## Integer Overflow -- SWC ID: 101 -- Type: Warning -- Contract: Unknown -- Function name: `_function_0x83f12fec` -- PC address: 158 - -### Description - -The arithmetic operation can result in integer overflow. - -## Integer Overflow -- SWC ID: 101 -- Type: Warning -- Contract: Unknown -- Function name: `_function_0x83f12fec` -- PC address: 278 - -### Description - -The arithmetic operation can result in integer overflow. - -## Integer Underflow -- SWC ID: 101 -- Type: Warning -- Contract: Unknown -- Function name: `_function_0x83f12fec` -- PC address: 378 - -### Description - -The substraction can result in an integer underflow. diff --git a/tests/testdata/outputs_expected/environments.sol.o.text b/tests/testdata/outputs_expected/environments.sol.o.text deleted file mode 100644 index ef673002..00000000 --- a/tests/testdata/outputs_expected/environments.sol.o.text +++ /dev/null @@ -1,30 +0,0 @@ -==== Integer Overflow ==== -SWC ID: 101 -Type: Warning -Contract: Unknown -Function name: _function_0x83f12fec -PC address: 158 -The arithmetic operation can result in integer overflow. - --------------------- - -==== Integer Overflow ==== -SWC ID: 101 -Type: Warning -Contract: Unknown -Function name: _function_0x83f12fec -PC address: 278 -The arithmetic operation can result in integer overflow. - --------------------- - -==== Integer Underflow ==== -SWC ID: 101 -Type: Warning -Contract: Unknown -Function name: _function_0x83f12fec -PC address: 378 -The substraction can result in an integer underflow. - --------------------- - diff --git a/tests/testdata/outputs_expected/ether_send.sol.o.jsonv2 b/tests/testdata/outputs_expected/ether_send.sol.o.jsonv2 index 3bce29c2..9f1597a1 100644 --- a/tests/testdata/outputs_expected/ether_send.sol.o.jsonv2 +++ b/tests/testdata/outputs_expected/ether_send.sol.o.jsonv2 @@ -1 +1,11 @@ -[{"issues": [], "meta": {}, "sourceFormat": "evm-byzantium-bytecode", "sourceList": ["0x3746c7c2ae7b0d4c3f8b1905df9a7ea169b9f93bec68a10a00b4c9d27a18c6fb"], "sourceType": "raw-bytecode"}] +[ + { + "issues": [], + "meta": {}, + "sourceFormat": "evm-byzantium-bytecode", + "sourceList": [ + "0x3746c7c2ae7b0d4c3f8b1905df9a7ea169b9f93bec68a10a00b4c9d27a18c6fb" + ], + "sourceType": "raw-bytecode" + } +] \ No newline at end of file diff --git a/tests/testdata/outputs_expected/kinds_of_calls.sol.o.jsonv2 b/tests/testdata/outputs_expected/kinds_of_calls.sol.o.jsonv2 index f7da0197..4f0d13e0 100644 --- a/tests/testdata/outputs_expected/kinds_of_calls.sol.o.jsonv2 +++ b/tests/testdata/outputs_expected/kinds_of_calls.sol.o.jsonv2 @@ -94,4 +94,4 @@ ], "sourceType": "raw-bytecode" } -] +] \ No newline at end of file diff --git a/tests/testdata/outputs_expected/metacoin.sol.o.jsonv2 b/tests/testdata/outputs_expected/metacoin.sol.o.jsonv2 index c669f304..40de69b4 100644 --- a/tests/testdata/outputs_expected/metacoin.sol.o.jsonv2 +++ b/tests/testdata/outputs_expected/metacoin.sol.o.jsonv2 @@ -1 +1,11 @@ -[{"issues": [], "meta": {}, "sourceFormat": "evm-byzantium-bytecode", "sourceList": ["0x0e6f727bb3301e02d3be831bf34357522fd2f1d40e90dff8e2214553b06b5f6c"], "sourceType": "raw-bytecode"}] +[ + { + "issues": [], + "meta": {}, + "sourceFormat": "evm-byzantium-bytecode", + "sourceList": [ + "0x0e6f727bb3301e02d3be831bf34357522fd2f1d40e90dff8e2214553b06b5f6c" + ], + "sourceType": "raw-bytecode" + } +] \ No newline at end of file diff --git a/tests/testdata/outputs_expected/nonascii.sol.o.jsonv2 b/tests/testdata/outputs_expected/nonascii.sol.o.jsonv2 index be2cc307..0667ad8c 100644 --- a/tests/testdata/outputs_expected/nonascii.sol.o.jsonv2 +++ b/tests/testdata/outputs_expected/nonascii.sol.o.jsonv2 @@ -1 +1,11 @@ -[{"issues": [], "meta": {}, "sourceFormat": "evm-byzantium-bytecode", "sourceList": ["0x11a78eb09819f505ba4f10747e6d1f7a44480e602c67573b7abac2f733a85d93"], "sourceType": "raw-bytecode"}] +[ + { + "issues": [], + "meta": {}, + "sourceFormat": "evm-byzantium-bytecode", + "sourceList": [ + "0x11a78eb09819f505ba4f10747e6d1f7a44480e602c67573b7abac2f733a85d93" + ], + "sourceType": "raw-bytecode" + } +] \ No newline at end of file diff --git a/tests/testdata/outputs_expected/overflow.sol.o.json b/tests/testdata/outputs_expected/overflow.sol.o.json index 89fdb839..c8d029f7 100644 --- a/tests/testdata/outputs_expected/overflow.sol.o.json +++ b/tests/testdata/outputs_expected/overflow.sol.o.json @@ -26,6 +26,19 @@ "sourceMap": null, "swc-id": "101", "title": "Integer Underflow" + }, + { + "address": 725, + "contract": "Unknown", + "debug": "", + "description": "The binary addition can overflow.\nThe operands of the addition operation are not sufficiently constrained. The addition could therefore result in an integer overflow. Prevent the overflow by checking inputs or ensure sure that the overflow is caught by an assertion.", + "function": "sendeth(address,uint256)", + "max_gas_used": 78155, + "min_gas_used": 17019, + "severity": "High", + "sourceMap": null, + "swc-id": "101", + "title": "Integer Overflow" } ], "success": true diff --git a/tests/testdata/outputs_expected/overflow.sol.o.jsonv2 b/tests/testdata/outputs_expected/overflow.sol.o.jsonv2 index dfcc29d5..570fdeba 100644 --- a/tests/testdata/outputs_expected/overflow.sol.o.jsonv2 +++ b/tests/testdata/outputs_expected/overflow.sol.o.jsonv2 @@ -34,6 +34,23 @@ "severity": "High", "swcID": "SWC-101", "swcTitle": "Integer Overflow and Underflow" + }, + { + "description": { + "head": "The binary addition can overflow.", + "tail": "The operands of the addition operation are not sufficiently constrained. The addition could therefore result in an integer overflow. Prevent the overflow by checking inputs or ensure sure that the overflow is caught by an assertion." + }, + "extra": { + "discoveryTime": "" + }, + "locations": [ + { + "sourceMap": "725:1:0" + } + ], + "severity": "High", + "swcID": "SWC-101", + "swcTitle": "Integer Overflow and Underflow" } ], "meta": {}, diff --git a/tests/testdata/outputs_expected/overflow.sol.o.markdown b/tests/testdata/outputs_expected/overflow.sol.o.markdown index 8774d894..82642a1e 100644 --- a/tests/testdata/outputs_expected/overflow.sol.o.markdown +++ b/tests/testdata/outputs_expected/overflow.sol.o.markdown @@ -25,3 +25,16 @@ The operands of the subtraction operation are not sufficiently constrained. The The binary subtraction can underflow. The operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion. + +## Integer Overflow +- SWC ID: 101 +- Severity: High +- Contract: Unknown +- Function name: `sendeth(address,uint256)` +- PC address: 725 +- Estimated Gas Usage: 17019 - 78155 + +### Description + +The binary addition can overflow. +The operands of the addition operation are not sufficiently constrained. The addition could therefore result in an integer overflow. Prevent the overflow by checking inputs or ensure sure that the overflow is caught by an assertion. diff --git a/tests/testdata/outputs_expected/overflow.sol.o.text b/tests/testdata/outputs_expected/overflow.sol.o.text index 698665d8..e70dda5b 100644 --- a/tests/testdata/outputs_expected/overflow.sol.o.text +++ b/tests/testdata/outputs_expected/overflow.sol.o.text @@ -20,3 +20,14 @@ The binary subtraction can underflow. The operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion. -------------------- +==== Integer Overflow ==== +SWC ID: 101 +Severity: High +Contract: Unknown +Function name: sendeth(address,uint256) +PC address: 725 +Estimated Gas Usage: 17019 - 78155 +The binary addition can overflow. +The operands of the addition operation are not sufficiently constrained. The addition could therefore result in an integer overflow. Prevent the overflow by checking inputs or ensure sure that the overflow is caught by an assertion. +-------------------- + diff --git a/tests/testdata/outputs_expected/rubixi.sol.o.json b/tests/testdata/outputs_expected/rubixi.sol.o.json deleted file mode 100644 index d328ade0..00000000 --- a/tests/testdata/outputs_expected/rubixi.sol.o.json +++ /dev/null @@ -1,166 +0,0 @@ -{ - "success": true, - "error": null, - "issues": [ - { - "title": "Ether send", - "description": "In the function `_function_0x4229616d` a non-zero amount of Ether is sent to an address taken from storage slot 5.\nThere is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`.\n\nThere is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`.\nThere is a check on storage index 1. This storage slot can be written to by calling the function `fallback`.", - "function": "_function_0x4229616d", - "type": "Warning", - "address": 1599, - "debug": "" - }, - { - "title": "Ether send", - "description": "In the function `_function_0xb4022950` a non-zero amount of Ether is sent to an address taken from storage slot 5.\nThere is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`.\n\nThere is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`.\nThere is a check on storage index 1. This storage slot can be written to by calling the function `fallback`.\nThere is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`.\nThere is a check on storage index 1. This storage slot can be written to by calling the function `fallback`.", - "function": "_function_0xb4022950", - "type": "Warning", - "address": 1940, - "debug": "" - }, - { - "title": "Ether send", - "description": "In the function `_function_0xb4022950` a non-zero amount of Ether is sent to an address taken from storage slot 5.\nThere is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`.\n\nThere is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`.\nThere is a check on storage index 1. This storage slot can be written to by calling the function `fallback`.\nThere is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`.\nThere is a check on storage index 1. This storage slot can be written to by calling the function `fallback`.\nThere is a check on storage index 1. This storage slot can be written to by calling the function `fallback`.", - "function": "_function_0xb4022950", - "type": "Warning", - "address": 2582, - "debug": "" - }, - { - "title": "Exception state", - "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. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking. ", - "function": "_function_0x57d4021b", - "type": "Informational", - "address": 1653, - "debug": "" - }, - { - "title": "Exception state", - "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. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking. ", - "function": "_function_0x9dbc4f9b", - "type": "Informational", - "address": 2085, - "debug": "" - }, - { - "title": "Invariant branch condition", - "description": "Found a conditional jump which always follows the same branch", - "function": "fallback", - "type": "Informational", - "address": 3111, - "debug": "" - }, - { - "title": "Invariant branch condition", - "description": "Found a conditional jump which always follows the same branch", - "function": "fallback", - "type": "Informational", - "address": 3140, - "debug": "" - }, - { - "title": "Invariant branch condition", - "description": "Found a conditional jump which always follows the same branch", - "function": "fallback", - "type": "Informational", - "address": 2950, - "debug": "" - }, - { - "title": "Invariant branch condition", - "description": "Found a conditional jump which always follows the same branch", - "function": "fallback", - "type": "Informational", - "address": 1268, - "debug": "" - }, - { - "title": "Invariant branch condition", - "description": "Found a conditional jump which always follows the same branch", - "function": "_function_0x09dfdc71", - "type": "Informational", - "address": 310, - "debug": "" - }, - { - "title": "Invariant branch condition", - "description": "Found a conditional jump which always follows the same branch", - "function": "_function_0x09dfdc71", - "type": "Informational", - "address": 1316, - "debug": "" - }, - { - "title": "Invariant branch condition", - "description": "Found a conditional jump which always follows the same branch", - "function": "_function_0x253459e3", - "type": "Informational", - "address": 1375, - "debug": "" - }, - { - "title": "Invariant branch condition", - "description": "Found a conditional jump which always follows the same branch", - "function": "_function_0x4229616d", - "type": "Informational", - "address": 1511, - "debug": "" - }, - { - "title": "Invariant branch condition", - "description": "Found a conditional jump which always follows the same branch", - "function": "_function_0x57d4021b", - "type": "Informational", - "address": 1679, - "debug": "" - }, - { - "title": "Invariant branch condition", - "description": "Found a conditional jump which always follows the same branch", - "function": "_function_0x6fbaaa1e", - "type": "Informational", - "address": 618, - "debug": "" - }, - { - "title": "Invariant branch condition", - "description": "Found a conditional jump which always follows the same branch", - "function": "_function_0x8a5fb3ca", - "type": "Informational", - "address": 805, - "debug": "" - }, - { - "title": "Invariant branch condition", - "description": "Found a conditional jump which always follows the same branch", - "function": "_function_0x9dbc4f9b", - "type": "Informational", - "address": 2187, - "debug": "" - }, - { - "title": "Unchecked CALL return value", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "_function_0x4229616d", - "type": "Informational", - "address": 1599, - "debug": "" - }, - { - "title": "Unchecked CALL return value", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "_function_0xb4022950", - "type": "Informational", - "address": 1940, - "debug": "" - }, - { - "title": "Unchecked CALL return value", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "_function_0xb4022950", - "type": "Informational", - "address": 2582, - "debug": "" - } - ] -} \ No newline at end of file diff --git a/tests/testdata/outputs_expected/rubixi.sol.o.markdown b/tests/testdata/outputs_expected/rubixi.sol.o.markdown deleted file mode 100644 index ba755c49..00000000 --- a/tests/testdata/outputs_expected/rubixi.sol.o.markdown +++ /dev/null @@ -1,238 +0,0 @@ -# Analysis results for test-filename.sol - -## Ether send - -- Type: Warning -- Contract: Unknown -- Function name: `_function_0x4229616d` -- PC address: 1599 - -### Description - -In the function `_function_0x4229616d` a non-zero amount of Ether is sent to an address taken from storage slot 5. -There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`. - -There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`. -There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`. - -## Ether send - -- Type: Warning -- Contract: Unknown -- Function name: `_function_0xb4022950` -- PC address: 1940 - -### Description - -In the function `_function_0xb4022950` a non-zero amount of Ether is sent to an address taken from storage slot 5. -There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`. - -There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`. -There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`. -There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`. -There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`. - -## Ether send - -- Type: Warning -- Contract: Unknown -- Function name: `_function_0xb4022950` -- PC address: 2582 - -### Description - -In the function `_function_0xb4022950` a non-zero amount of Ether is sent to an address taken from storage slot 5. -There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`. - -There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`. -There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`. -There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`. -There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`. -There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`. - -## Exception state - -- Type: Informational -- Contract: Unknown -- Function name: `_function_0x57d4021b` -- PC address: 1653 - -### 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. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking. - -## Exception state - -- Type: Informational -- Contract: Unknown -- Function name: `_function_0x9dbc4f9b` -- PC address: 2085 - -### 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. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking. - -## Tautology - -- Type: Informational -- Contract: Unknown -- Function name: `fallback` -- PC address: 3111 - -### Description - -Found a conditional jump which always follows the same branch, value: True - -## Tautology - -- Type: Informational -- Contract: Unknown -- Function name: `fallback` -- PC address: 3140 - -### Description - -Found a conditional jump which always follows the same branch, value: True - -## Tautology - -- Type: Informational -- Contract: Unknown -- Function name: `fallback` -- PC address: 2950 - -### Description - -Found a conditional jump which always follows the same branch, value: True - -## Tautology - -- Type: Informational -- Contract: Unknown -- Function name: `fallback` -- PC address: 1268 - -### Description - -Found a conditional jump which always follows the same branch, value: True - -## Tautology - -- Type: Informational -- Contract: Unknown -- Function name: `_function_0x09dfdc71` -- PC address: 310 - -### Description - -Found a conditional jump which always follows the same branch, value: False - -## Tautology - -- Type: Informational -- Contract: Unknown -- Function name: `_function_0x09dfdc71` -- PC address: 1316 - -### Description - -Found a conditional jump which always follows the same branch, value: True - -## Tautology - -- Type: Informational -- Contract: Unknown -- Function name: `_function_0x253459e3` -- PC address: 1375 - -### Description - -Found a conditional jump which always follows the same branch, value: True - -## Tautology - -- Type: Informational -- Contract: Unknown -- Function name: `_function_0x4229616d` -- PC address: 1511 - -### Description - -Found a conditional jump which always follows the same branch, value: True - -## Tautology - -- Type: Informational -- Contract: Unknown -- Function name: `_function_0x57d4021b` -- PC address: 1679 - -### Description - -Found a conditional jump which always follows the same branch, value: True - -## Tautology - -- Type: Informational -- Contract: Unknown -- Function name: `_function_0x6fbaaa1e` -- PC address: 618 - -### Description - -Found a conditional jump which always follows the same branch, value: False - -## Tautology - -- Type: Informational -- Contract: Unknown -- Function name: `_function_0x8a5fb3ca` -- PC address: 805 - -### Description - -Found a conditional jump which always follows the same branch, value: False - -## Tautology - -- Type: Informational -- Contract: Unknown -- Function name: `_function_0x9dbc4f9b` -- PC address: 2187 - -### Description - -Found a conditional jump which always follows the same branch, value: True - -## Unchecked CALL return value - -- Type: Informational -- Contract: Unknown -- Function name: `_function_0x4229616d` -- PC address: 1599 - -### Description - -The return value of an external call is not checked. Note that execution continue even if the called contract throws. - -## Unchecked CALL return value - -- Type: Informational -- Contract: Unknown -- Function name: `_function_0xb4022950` -- PC address: 1940 - -### Description - -The return value of an external call is not checked. Note that execution continue even if the called contract throws. - -## Unchecked CALL return value - -- Type: Informational -- Contract: Unknown -- Function name: `_function_0xb4022950` -- PC address: 2582 - -### Description - -The return value of an external call is not checked. Note that execution continue even if the called contract throws. \ No newline at end of file diff --git a/tests/testdata/outputs_expected/rubixi.sol.o.text b/tests/testdata/outputs_expected/rubixi.sol.o.text deleted file mode 100644 index 80a40a7f..00000000 --- a/tests/testdata/outputs_expected/rubixi.sol.o.text +++ /dev/null @@ -1,177 +0,0 @@ -==== Ether send ==== -Type: Warning -Contract: Unknown -Function name: _function_0x4229616d -PC address: 1599 -In the function `_function_0x4229616d` a non-zero amount of Ether is sent to an address taken from storage slot 5. -There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`. - -There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`. -There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`. --------------------- - -==== Ether send ==== -Type: Warning -Contract: Unknown -Function name: _function_0xb4022950 -PC address: 1940 -In the function `_function_0xb4022950` a non-zero amount of Ether is sent to an address taken from storage slot 5. -There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`. - -There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`. -There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`. -There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`. -There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`. --------------------- - -==== Ether send ==== -Type: Warning -Contract: Unknown -Function name: _function_0xb4022950 -PC address: 2582 -In the function `_function_0xb4022950` a non-zero amount of Ether is sent to an address taken from storage slot 5. -There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`. - -There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`. -There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`. -There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`. -There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`. -There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`. --------------------- - -==== Exception state ==== -Type: Informational -Contract: Unknown -Function name: _function_0x57d4021b -PC address: 1653 -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. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking. --------------------- - -==== Exception state ==== -Type: Informational -Contract: Unknown -Function name: _function_0x9dbc4f9b -PC address: 2085 -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. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking. --------------------- - -==== Tautology ==== -Type: Informational -Contract: Unknown -Function name: fallback -PC address: 3111 -Found a conditional jump which always follows the same branch, value: True --------------------- - -==== Tautology ==== -Type: Informational -Contract: Unknown -Function name: fallback -PC address: 3140 -Found a conditional jump which always follows the same branch, value: True --------------------- - -==== Tautology ==== -Type: Informational -Contract: Unknown -Function name: fallback -PC address: 2950 -Found a conditional jump which always follows the same branch, value: True --------------------- - -==== Tautology ==== -Type: Informational -Contract: Unknown -Function name: fallback -PC address: 1268 -Found a conditional jump which always follows the same branch, value: True --------------------- - -==== Tautology ==== -Type: Informational -Contract: Unknown -Function name: _function_0x09dfdc71 -PC address: 310 -Found a conditional jump which always follows the same branch, value: False --------------------- - -==== Tautology ==== -Type: Informational -Contract: Unknown -Function name: _function_0x09dfdc71 -PC address: 1316 -Found a conditional jump which always follows the same branch, value: True --------------------- - -==== Tautology ==== -Type: Informational -Contract: Unknown -Function name: _function_0x253459e3 -PC address: 1375 -Found a conditional jump which always follows the same branch, value: True --------------------- - -==== Tautology ==== -Type: Informational -Contract: Unknown -Function name: _function_0x4229616d -PC address: 1511 -Found a conditional jump which always follows the same branch, value: True --------------------- - -==== Tautology ==== -Type: Informational -Contract: Unknown -Function name: _function_0x57d4021b -PC address: 1679 -Found a conditional jump which always follows the same branch, value: True --------------------- - -==== Tautology ==== -Type: Informational -Contract: Unknown -Function name: _function_0x6fbaaa1e -PC address: 618 -Found a conditional jump which always follows the same branch, value: False --------------------- - -==== Tautology ==== -Type: Informational -Contract: Unknown -Function name: _function_0x8a5fb3ca -PC address: 805 -Found a conditional jump which always follows the same branch, value: False --------------------- - -==== Tautology ==== -Type: Informational -Contract: Unknown -Function name: _function_0x9dbc4f9b -PC address: 2187 -Found a conditional jump which always follows the same branch, value: True --------------------- - -==== Unchecked CALL return value ==== -Type: Informational -Contract: Unknown -Function name: _function_0x4229616d -PC address: 1599 -The return value of an external call is not checked. Note that execution continue even if the called contract throws. --------------------- - -==== Unchecked CALL return value ==== -Type: Informational -Contract: Unknown -Function name: _function_0xb4022950 -PC address: 1940 -The return value of an external call is not checked. Note that execution continue even if the called contract throws. --------------------- - -==== Unchecked CALL return value ==== -Type: Informational -Contract: Unknown -Function name: _function_0xb4022950 -PC address: 2582 -The return value of an external call is not checked. Note that execution continue even if the called contract throws. --------------------- - diff --git a/tests/testdata/outputs_expected/underflow.sol.o.json b/tests/testdata/outputs_expected/underflow.sol.o.json index 6a2464d5..a0042598 100644 --- a/tests/testdata/outputs_expected/underflow.sol.o.json +++ b/tests/testdata/outputs_expected/underflow.sol.o.json @@ -26,6 +26,19 @@ "sourceMap": null, "swc-id": "101", "title": "Integer Underflow" + }, + { + "address": 725, + "contract": "Unknown", + "debug": "", + "description": "The binary addition can overflow.\nThe operands of the addition operation are not sufficiently constrained. The addition could therefore result in an integer overflow. Prevent the overflow by checking inputs or ensure sure that the overflow is caught by an assertion.", + "function": "sendeth(address,uint256)", + "max_gas_used": 52861, + "min_gas_used": 11915, + "severity": "High", + "sourceMap": null, + "swc-id": "101", + "title": "Integer Overflow" } ], "success": true diff --git a/tests/testdata/outputs_expected/underflow.sol.o.jsonv2 b/tests/testdata/outputs_expected/underflow.sol.o.jsonv2 index 94854e04..b6611bdd 100644 --- a/tests/testdata/outputs_expected/underflow.sol.o.jsonv2 +++ b/tests/testdata/outputs_expected/underflow.sol.o.jsonv2 @@ -34,6 +34,23 @@ "severity": "High", "swcID": "SWC-101", "swcTitle": "Integer Overflow and Underflow" + }, + { + "description": { + "head": "The binary addition can overflow.", + "tail": "The operands of the addition operation are not sufficiently constrained. The addition could therefore result in an integer overflow. Prevent the overflow by checking inputs or ensure sure that the overflow is caught by an assertion." + }, + "extra": { + "discoveryTime": "" + }, + "locations": [ + { + "sourceMap": "725:1:0" + } + ], + "severity": "High", + "swcID": "SWC-101", + "swcTitle": "Integer Overflow and Underflow" } ], "meta": {}, diff --git a/tests/testdata/outputs_expected/underflow.sol.o.markdown b/tests/testdata/outputs_expected/underflow.sol.o.markdown index 48399072..acc444d4 100644 --- a/tests/testdata/outputs_expected/underflow.sol.o.markdown +++ b/tests/testdata/outputs_expected/underflow.sol.o.markdown @@ -25,3 +25,16 @@ The operands of the subtraction operation are not sufficiently constrained. The The binary subtraction can underflow. The operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion. + +## Integer Overflow +- SWC ID: 101 +- Severity: High +- Contract: Unknown +- Function name: `sendeth(address,uint256)` +- PC address: 725 +- Estimated Gas Usage: 11915 - 52861 + +### Description + +The binary addition can overflow. +The operands of the addition operation are not sufficiently constrained. The addition could therefore result in an integer overflow. Prevent the overflow by checking inputs or ensure sure that the overflow is caught by an assertion. diff --git a/tests/testdata/outputs_expected/underflow.sol.o.text b/tests/testdata/outputs_expected/underflow.sol.o.text index 16701a04..498ff588 100644 --- a/tests/testdata/outputs_expected/underflow.sol.o.text +++ b/tests/testdata/outputs_expected/underflow.sol.o.text @@ -20,3 +20,14 @@ The binary subtraction can underflow. The operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion. -------------------- +==== Integer Overflow ==== +SWC ID: 101 +Severity: High +Contract: Unknown +Function name: sendeth(address,uint256) +PC address: 725 +Estimated Gas Usage: 11915 - 52861 +The binary addition can overflow. +The operands of the addition operation are not sufficiently constrained. The addition could therefore result in an integer overflow. Prevent the overflow by checking inputs or ensure sure that the overflow is caught by an assertion. +-------------------- + diff --git a/tests/testdata/outputs_expected/weak_random.sol.o.json b/tests/testdata/outputs_expected/weak_random.sol.o.json deleted file mode 100644 index 2338b178..00000000 --- a/tests/testdata/outputs_expected/weak_random.sol.o.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "success": true, - "error": null, - "issues": [ - { - "title": "Dependence on predictable environment variable", - "description": "In the function `_function_0xe9874106` the following predictable state variables are used to determine Ether recipient:\n- block.coinbase\n", - "function": "_function_0xe9874106", - "type": "Warning", - "address": 1285, - "debug": "" - }, - { - "title": "Ether send", - "description": "In the function `_function_0xe9874106` a non-zero amount of Ether is sent to an address taken from storage slot 0.\nThere is a check on storage index 0. This storage slot can be written to by calling the function `fallback`.\n\nThere is a check on storage index 1. This storage slot can be written to by calling the function `fallback`.\nThere is a check on storage index 1. This storage slot can be written to by calling the function `fallback`.", - "function": "_function_0xe9874106", - "type": "Warning", - "address": 1285, - "debug": "" - }, - { - "title": "Exception state", - "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. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking. ", - "function": "fallback", - "type": "Informational", - "address": 356, - "debug": "" - }, - { - "title": "Exception state", - "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. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking. ", - "function": "_function_0xe9874106", - "type": "Informational", - "address": 146, - "debug": "" - }, - { - "title": "Transaction order dependence", - "description": "A possible transaction order independence vulnerability exists in function _function_0xe9874106. The value or direction of the call statement is determined from a tainted storage location", - "function": "_function_0xe9874106", - "type": "Warning", - "address": 1285, - "debug": "" - } - ] -} \ No newline at end of file diff --git a/tests/testdata/outputs_expected/weak_random.sol.o.markdown b/tests/testdata/outputs_expected/weak_random.sol.o.markdown deleted file mode 100644 index b5744566..00000000 --- a/tests/testdata/outputs_expected/weak_random.sol.o.markdown +++ /dev/null @@ -1,62 +0,0 @@ -# Analysis results for test-filename.sol - -## Dependence on predictable environment variable - -- Type: Warning -- Contract: Unknown -- Function name: `_function_0xe9874106` -- PC address: 1285 - -### Description - -In the function `_function_0xe9874106` the following predictable state variables are used to determine Ether recipient: -- block.coinbase - - -## Ether send - -- Type: Warning -- Contract: Unknown -- Function name: `_function_0xe9874106` -- PC address: 1285 - -### Description - -In the function `_function_0xe9874106` a non-zero amount of Ether is sent to an address taken from storage slot 0. -There is a check on storage index 0. This storage slot can be written to by calling the function `fallback`. - -There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`. -There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`. - -## Exception state - -- Type: Informational -- Contract: Unknown -- Function name: `fallback` -- PC address: 356 - -### 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. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking. - -## Exception state - -- Type: Informational -- Contract: Unknown -- Function name: `_function_0xe9874106` -- PC address: 146 - -### 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. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking. - -## Transaction order dependence - -- Type: Warning -- Contract: Unknown -- Function name: `_function_0xe9874106` -- PC address: 1285 - -### Description - -A possible transaction order independence vulnerability exists in function _function_0xe9874106. The value or direction of the call statement is determined from a tainted storage location \ No newline at end of file diff --git a/tests/testdata/outputs_expected/weak_random.sol.o.text b/tests/testdata/outputs_expected/weak_random.sol.o.text deleted file mode 100644 index 9e105cfe..00000000 --- a/tests/testdata/outputs_expected/weak_random.sol.o.text +++ /dev/null @@ -1,46 +0,0 @@ -==== Dependence on predictable environment variable ==== -Type: Warning -Contract: Unknown -Function name: _function_0xe9874106 -PC address: 1285 -In the function `_function_0xe9874106` the following predictable state variables are used to determine Ether recipient: -- block.coinbase - --------------------- - -==== Ether send ==== -Type: Warning -Contract: Unknown -Function name: _function_0xe9874106 -PC address: 1285 -In the function `_function_0xe9874106` a non-zero amount of Ether is sent to an address taken from storage slot 0. -There is a check on storage index 0. This storage slot can be written to by calling the function `fallback`. - -There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`. -There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`. --------------------- - -==== Exception state ==== -Type: Informational -Contract: Unknown -Function name: fallback -PC address: 356 -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. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking. --------------------- - -==== Exception state ==== -Type: Informational -Contract: Unknown -Function name: _function_0xe9874106 -PC address: 146 -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. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking. --------------------- - -==== Transaction order dependence ==== -Type: Warning -Contract: Unknown -Function name: _function_0xe9874106 -PC address: 1285 -A possible transaction order independence vulnerability exists in function _function_0xe9874106. The value or direction of the call statement is determined from a tainted storage location --------------------- -