From c3a453edaaf371683cc0ca10f14bac4458593d74 Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Tue, 6 Nov 2018 23:26:09 +0700 Subject: [PATCH 01/41] Add the much cooler "total downloads" badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b975def..2b3c0fda 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ ![Master Build Status](https://img.shields.io/circleci/project/github/ConsenSys/mythril-classic/master.svg) [![Waffle.io - Columns and their card count](https://badge.waffle.io/ConsenSys/mythril-classic.svg?columns=In%20Progress)](https://waffle.io/ConsenSys/mythril-classic/) [![Sonarcloud - Maintainability](https://sonarcloud.io/api/project_badges/measure?project=mythril&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=mythril) -[![PyPI Statistics](https://pypistats.com/badge/mythril.svg)](https://pypistats.com/package/mythril) +[![Downloads](https://pepy.tech/badge/mythril)](https://pepy.tech/project/mythril) Mythril Classic is an open-source security analysis tool for Ethereum smart contracts. It uses concolic analysis, taint analysis and control flow checking to detect a variety of security vulnerabilities. From 8a526ad48e377c506ebfd86987e49f2613477227 Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Mon, 26 Nov 2018 14:25:00 +0700 Subject: [PATCH 02/41] Refactor to concrete senders --- mythril/analysis/analysis_utils.py | 48 ------------------- mythril/analysis/modules/ether_thief.py | 14 +----- mythril/analysis/modules/suicide.py | 15 +----- .../laser/ethereum/transaction/symbolic.py | 10 ++-- 4 files changed, 9 insertions(+), 78 deletions(-) delete mode 100644 mythril/analysis/analysis_utils.py diff --git a/mythril/analysis/analysis_utils.py b/mythril/analysis/analysis_utils.py deleted file mode 100644 index f4fc8e29..00000000 --- a/mythril/analysis/analysis_utils.py +++ /dev/null @@ -1,48 +0,0 @@ -import re -from typing import List -from z3 import * -from mythril.laser.ethereum.transaction import ContractCreationTransaction -from mythril.laser.ethereum.state.global_state import GlobalState - - -def get_non_creator_constraints(state: GlobalState) -> (List, bool): - """ - Get constraints which say that the caller isn't the creator of the contract - :param state: The state - :return: tuple of (constraints, bool) where the bool says whether the caller is constrained or not - """ - not_creator_constraints = [] - creator = None - if isinstance( - state.world_state.transaction_sequence[0], ContractCreationTransaction - ): - creator = state.world_state.transaction_sequence[0].caller - - if creator is not None: - for transaction in state.world_state.transaction_sequence[1:]: - not_creator_constraints.append( - Not(Extract(159, 0, transaction.caller) == Extract(159, 0, creator)) - ) - not_creator_constraints.append( - Not(Extract(159, 0, transaction.caller) == 0) - ) - else: - for transaction in state.world_state.transaction_sequence: - not_creator_constraints.append( - Not(Extract(159, 0, transaction.caller) == 0) - ) - if not has_caller_check_constraint(state.mstate.constraints): - return [], True - return not_creator_constraints, False - - -def has_caller_check_constraint(constraints: List) -> bool: - """ - Checks whether the caller is constrained to a value or not - """ - for constraint in constraints: - if re.search( - r"caller.*==[0-9]{20}", str(constraint).replace("\n", "").replace(" ", "") - ): - return False - return True diff --git a/mythril/analysis/modules/ether_thief.py b/mythril/analysis/modules/ether_thief.py index c304fb11..f70973a1 100644 --- a/mythril/analysis/modules/ether_thief.py +++ b/mythril/analysis/modules/ether_thief.py @@ -1,6 +1,5 @@ from mythril.analysis.ops import * from mythril.analysis import solver -from mythril.analysis.analysis_utils import get_non_creator_constraints from mythril.analysis.report import Issue from mythril.analysis.swc_data import UNPROTECTED_ETHER_WITHDRAWAL from mythril.analysis.modules.base import DetectionModule @@ -22,8 +21,6 @@ An issue is reported if: """ -ARBITRARY_SENDER_ADDRESS = 0xAAAAAAAABBBBBBBBBCCCCCCCDDDDDDDDEEEEEEEE - class EtherThief(DetectionModule): def __init__(self): @@ -56,10 +53,6 @@ class EtherThief(DetectionModule): call_value = state.mstate.stack[-3] target = state.mstate.stack[-2] - not_creator_constraints, constrained = get_non_creator_constraints(state) - if constrained: - return [] - eth_sent_total = BitVecVal(0, 256) for tx in state.world_state.transaction_sequence[1:]: @@ -70,12 +63,7 @@ class EtherThief(DetectionModule): transaction_sequence = solver.get_transaction_sequence( state, node.constraints - + not_creator_constraints - + [ - UGT(call_value, eth_sent_total), - state.environment.sender == ARBITRARY_SENDER_ADDRESS, - target == state.environment.sender, - ], + + [UGT(call_value, eth_sent_total), target == state.environment.sender], ) debug = "Transaction Sequence: " + str(transaction_sequence) diff --git a/mythril/analysis/modules/suicide.py b/mythril/analysis/modules/suicide.py index d49d1e0c..7a21b990 100644 --- a/mythril/analysis/modules/suicide.py +++ b/mythril/analysis/modules/suicide.py @@ -1,5 +1,4 @@ from mythril.analysis import solver -from mythril.analysis.analysis_utils import get_non_creator_constraints from mythril.analysis.ops import * from mythril.analysis.report import Issue from mythril.analysis.swc_data import UNPROTECTED_SELFDESTRUCT @@ -13,8 +12,6 @@ Check if the contact can be 'accidentally' killed by anyone. For kill-able contracts, also check whether it is possible to direct the contract balance to the attacker. """ -ARBITRARY_SENDER_ADDRESS = 0xAAAAAAAABBBBBBBBBCCCCCCCDDDDDDDDEEEEEEEE - def _analyze_state(state): logging.info("Suicide module: Analyzing suicide instruction") @@ -24,20 +21,10 @@ def _analyze_state(state): logging.debug("[SUICIDE] SUICIDE in function " + node.function_name) - not_creator_constraints, constrained = get_non_creator_constraints(state) - constraints = ( - node.constraints - + not_creator_constraints - + [state.environment.sender == ARBITRARY_SENDER_ADDRESS] - ) - - if constrained: - return [] - try: try: transaction_sequence = solver.get_transaction_sequence( - state, constraints + [to == ARBITRARY_SENDER_ADDRESS] + state, node.constraints ) description = "Anyone can kill this contract and withdraw its balance to their own account." except UnsatError: diff --git a/mythril/laser/ethereum/transaction/symbolic.py b/mythril/laser/ethereum/transaction/symbolic.py index 88e16780..6d58d195 100644 --- a/mythril/laser/ethereum/transaction/symbolic.py +++ b/mythril/laser/ethereum/transaction/symbolic.py @@ -1,4 +1,4 @@ -from z3 import BitVec, Extract, Not +from z3 import BitVec, BitVecVal from logging import debug from mythril.disassembler.disassembly import Disassembly @@ -12,6 +12,10 @@ from mythril.laser.ethereum.transaction.transaction_models import ( ) +CREATOR_ADDRESS = 0xAFFEAFFEAFFEAFFEAFFEAFFEAFFEAFFEAFFEAFFE +ATTACKER_ADDRESS = 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF + + def execute_message_call(laser_evm, callee_address: str) -> None: """ Executes a message call transaction from all open states """ # TODO: Resolve circular import between .transaction and ..svm to import LaserEVM here @@ -30,7 +34,7 @@ def execute_message_call(laser_evm, callee_address: str) -> None: gas_price=BitVec("gas_price{}".format(next_transaction_id), 256), gas_limit=8000000, # block gas limit origin=BitVec("origin{}".format(next_transaction_id), 256), - caller=BitVec("caller{}".format(next_transaction_id), 256), + caller=BitVecVal(ATTACKER_ADDRESS, 256), callee_account=open_world_state[callee_address], call_data=Calldata(next_transaction_id), call_data_type=CalldataType.SYMBOLIC, @@ -64,7 +68,7 @@ def execute_contract_creation( gas_limit=8000000, # block gas limit origin=BitVec("origin{}".format(next_transaction_id), 256), code=Disassembly(contract_initialization_code), - caller=BitVec("creator{}".format(next_transaction_id), 256), + caller=CREATOR_ADDRESS, callee_account=new_account, call_data=[], call_data_type=CalldataType.SYMBOLIC, From ebf4eaa51c632adc077bc3fc4c245779149358c6 Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Mon, 26 Nov 2018 14:33:24 +0700 Subject: [PATCH 03/41] Remove crappy metacoin sample --- tests/testdata/inputs/metacoin.sol.o | 1 - .../outputs_expected/metacoin.sol.o.easm | 253 ------------------ .../metacoin.sol.o.graph.html | 62 ----- .../outputs_expected/metacoin.sol.o.json | 5 - .../outputs_expected/metacoin.sol.o.markdown | 3 - .../outputs_expected/metacoin.sol.o.text | 1 - 6 files changed, 325 deletions(-) delete mode 100644 tests/testdata/inputs/metacoin.sol.o delete mode 100644 tests/testdata/outputs_expected/metacoin.sol.o.easm delete mode 100644 tests/testdata/outputs_expected/metacoin.sol.o.graph.html delete mode 100644 tests/testdata/outputs_expected/metacoin.sol.o.json delete mode 100644 tests/testdata/outputs_expected/metacoin.sol.o.markdown delete mode 100644 tests/testdata/outputs_expected/metacoin.sol.o.text diff --git a/tests/testdata/inputs/metacoin.sol.o b/tests/testdata/inputs/metacoin.sol.o deleted file mode 100644 index 87e952f3..00000000 --- a/tests/testdata/inputs/metacoin.sol.o +++ /dev/null @@ -1 +0,0 @@ -60606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806327e235e314610051578063412664ae1461009e575b600080fd5b341561005c57600080fd5b610088600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506100f8565b6040518082815260200191505060405180910390f35b34156100a957600080fd5b6100de600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610110565b604051808215151515815260200191505060405180910390f35b60006020528060005260406000206000915090505481565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101561016157600090506101fe565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550600090505b929150505600a165627a7a72305820c860d60246e215343f02c5025aeef4ad1f207b0a7d2dec05e43f6ecaaebe9cec0029 \ No newline at end of file diff --git a/tests/testdata/outputs_expected/metacoin.sol.o.easm b/tests/testdata/outputs_expected/metacoin.sol.o.easm deleted file mode 100644 index fbb53181..00000000 --- a/tests/testdata/outputs_expected/metacoin.sol.o.easm +++ /dev/null @@ -1,253 +0,0 @@ -0 PUSH1 0x60 -2 PUSH1 0x40 -4 MSTORE -5 PUSH1 0x04 -7 CALLDATASIZE -8 LT -9 PUSH2 0x004c -12 JUMPI -13 PUSH1 0x00 -15 CALLDATALOAD -16 PUSH29 0x0100000000000000000000000000000000000000000000000000000000 -46 SWAP1 -47 DIV -48 PUSH4 0xffffffff -53 AND -54 DUP1 -55 PUSH4 0x27e235e3 -60 EQ -61 PUSH2 0x0051 -64 JUMPI -65 DUP1 -66 PUSH4 0x412664ae -71 EQ -72 PUSH2 0x009e -75 JUMPI -76 JUMPDEST -77 PUSH1 0x00 -79 DUP1 -80 REVERT -81 JUMPDEST -82 CALLVALUE -83 ISZERO -84 PUSH2 0x005c -87 JUMPI -88 PUSH1 0x00 -90 DUP1 -91 REVERT -92 JUMPDEST -93 PUSH2 0x0088 -96 PUSH1 0x04 -98 DUP1 -99 DUP1 -100 CALLDATALOAD -101 PUSH20 0xffffffffffffffffffffffffffffffffffffffff -122 AND -123 SWAP1 -124 PUSH1 0x20 -126 ADD -127 SWAP1 -128 SWAP2 -129 SWAP1 -130 POP -131 POP -132 PUSH2 0x00f8 -135 JUMP -136 JUMPDEST -137 PUSH1 0x40 -139 MLOAD -140 DUP1 -141 DUP3 -142 DUP2 -143 MSTORE -144 PUSH1 0x20 -146 ADD -147 SWAP2 -148 POP -149 POP -150 PUSH1 0x40 -152 MLOAD -153 DUP1 -154 SWAP2 -155 SUB -156 SWAP1 -157 RETURN -158 JUMPDEST -159 CALLVALUE -160 ISZERO -161 PUSH2 0x00a9 -164 JUMPI -165 PUSH1 0x00 -167 DUP1 -168 REVERT -169 JUMPDEST -170 PUSH2 0x00de -173 PUSH1 0x04 -175 DUP1 -176 DUP1 -177 CALLDATALOAD -178 PUSH20 0xffffffffffffffffffffffffffffffffffffffff -199 AND -200 SWAP1 -201 PUSH1 0x20 -203 ADD -204 SWAP1 -205 SWAP2 -206 SWAP1 -207 DUP1 -208 CALLDATALOAD -209 SWAP1 -210 PUSH1 0x20 -212 ADD -213 SWAP1 -214 SWAP2 -215 SWAP1 -216 POP -217 POP -218 PUSH2 0x0110 -221 JUMP -222 JUMPDEST -223 PUSH1 0x40 -225 MLOAD -226 DUP1 -227 DUP3 -228 ISZERO -229 ISZERO -230 ISZERO -231 ISZERO -232 DUP2 -233 MSTORE -234 PUSH1 0x20 -236 ADD -237 SWAP2 -238 POP -239 POP -240 PUSH1 0x40 -242 MLOAD -243 DUP1 -244 SWAP2 -245 SUB -246 SWAP1 -247 RETURN -248 JUMPDEST -249 PUSH1 0x00 -251 PUSH1 0x20 -253 MSTORE -254 DUP1 -255 PUSH1 0x00 -257 MSTORE -258 PUSH1 0x40 -260 PUSH1 0x00 -262 SHA3 -263 PUSH1 0x00 -265 SWAP2 -266 POP -267 SWAP1 -268 POP -269 SLOAD -270 DUP2 -271 JUMP -272 JUMPDEST -273 PUSH1 0x00 -275 DUP2 -276 PUSH1 0x00 -278 DUP1 -279 CALLER -280 PUSH20 0xffffffffffffffffffffffffffffffffffffffff -301 AND -302 PUSH20 0xffffffffffffffffffffffffffffffffffffffff -323 AND -324 DUP2 -325 MSTORE -326 PUSH1 0x20 -328 ADD -329 SWAP1 -330 DUP2 -331 MSTORE -332 PUSH1 0x20 -334 ADD -335 PUSH1 0x00 -337 SHA3 -338 SLOAD -339 LT -340 ISZERO -341 PUSH2 0x0161 -344 JUMPI -345 PUSH1 0x00 -347 SWAP1 -348 POP -349 PUSH2 0x01fe -352 JUMP -353 JUMPDEST -354 DUP2 -355 PUSH1 0x00 -357 DUP1 -358 CALLER -359 PUSH20 0xffffffffffffffffffffffffffffffffffffffff -380 AND -381 PUSH20 0xffffffffffffffffffffffffffffffffffffffff -402 AND -403 DUP2 -404 MSTORE -405 PUSH1 0x20 -407 ADD -408 SWAP1 -409 DUP2 -410 MSTORE -411 PUSH1 0x20 -413 ADD -414 PUSH1 0x00 -416 SHA3 -417 PUSH1 0x00 -419 DUP3 -420 DUP3 -421 SLOAD -422 SUB -423 SWAP3 -424 POP -425 POP -426 DUP2 -427 SWAP1 -428 SSTORE -429 POP -430 DUP2 -431 PUSH1 0x00 -433 DUP1 -434 DUP6 -435 PUSH20 0xffffffffffffffffffffffffffffffffffffffff -456 AND -457 PUSH20 0xffffffffffffffffffffffffffffffffffffffff -478 AND -479 DUP2 -480 MSTORE -481 PUSH1 0x20 -483 ADD -484 SWAP1 -485 DUP2 -486 MSTORE -487 PUSH1 0x20 -489 ADD -490 PUSH1 0x00 -492 SHA3 -493 PUSH1 0x00 -495 DUP3 -496 DUP3 -497 SLOAD -498 ADD -499 SWAP3 -500 POP -501 POP -502 DUP2 -503 SWAP1 -504 SSTORE -505 POP -506 PUSH1 0x00 -508 SWAP1 -509 POP -510 JUMPDEST -511 SWAP3 -512 SWAP2 -513 POP -514 POP -515 JUMP -516 STOP diff --git a/tests/testdata/outputs_expected/metacoin.sol.o.graph.html b/tests/testdata/outputs_expected/metacoin.sol.o.graph.html deleted file mode 100644 index 6ee81b9c..00000000 --- a/tests/testdata/outputs_expected/metacoin.sol.o.graph.html +++ /dev/null @@ -1,62 +0,0 @@ - - - - Call Graph - - - - - - - - - - -

Mythril / Ethereum LASER Symbolic VM

-
- - - diff --git a/tests/testdata/outputs_expected/metacoin.sol.o.json b/tests/testdata/outputs_expected/metacoin.sol.o.json deleted file mode 100644 index 179ed787..00000000 --- a/tests/testdata/outputs_expected/metacoin.sol.o.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "error": null, - "issues": [], - "success": true -} diff --git a/tests/testdata/outputs_expected/metacoin.sol.o.markdown b/tests/testdata/outputs_expected/metacoin.sol.o.markdown deleted file mode 100644 index 321484fd..00000000 --- a/tests/testdata/outputs_expected/metacoin.sol.o.markdown +++ /dev/null @@ -1,3 +0,0 @@ -# Analysis results for None - -The analysis was completed successfully. No issues were detected. diff --git a/tests/testdata/outputs_expected/metacoin.sol.o.text b/tests/testdata/outputs_expected/metacoin.sol.o.text deleted file mode 100644 index 729320d8..00000000 --- a/tests/testdata/outputs_expected/metacoin.sol.o.text +++ /dev/null @@ -1 +0,0 @@ -The analysis was completed successfully. No issues were detected. From f6dc874226910cdc2841bfe95a0f764a4841576a Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Mon, 26 Nov 2018 18:26:19 +0700 Subject: [PATCH 04/41] Return 0x00 for unitialized storage, Ether Thief fixes --- mythril/analysis/modules/ether_thief.py | 22 ++++++++++++++-------- mythril/laser/ethereum/state/account.py | 4 ++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/mythril/analysis/modules/ether_thief.py b/mythril/analysis/modules/ether_thief.py index f70973a1..90a6d402 100644 --- a/mythril/analysis/modules/ether_thief.py +++ b/mythril/analysis/modules/ether_thief.py @@ -4,7 +4,7 @@ from mythril.analysis.report import Issue from mythril.analysis.swc_data import UNPROTECTED_ETHER_WITHDRAWAL from mythril.analysis.modules.base import DetectionModule from mythril.exceptions import UnsatError -from z3 import BitVecVal, UGT +from z3 import BitVecVal, UGT, Sum import logging @@ -55,16 +55,22 @@ class EtherThief(DetectionModule): eth_sent_total = BitVecVal(0, 256) - for tx in state.world_state.transaction_sequence[1:]: - eth_sent_total += tx.call_value + constraints = node.constraints + + for tx in state.world_state.transaction_sequence: + if tx.caller == 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF: + + # There's sometimes no overflow check on balances added. + # But we don't care about attacks that require more 2^^256 ETH to be sent. + + constraints += [BVAddNoOverflow(eth_sent_total, tx.call_value, False)] + eth_sent_total = Sum(eth_sent_total, tx.call_value) + + constraints += [UGT(call_value, eth_sent_total), target == state.environment.sender] try: - transaction_sequence = solver.get_transaction_sequence( - state, - node.constraints - + [UGT(call_value, eth_sent_total), target == state.environment.sender], - ) + transaction_sequence = solver.get_transaction_sequence(state, constraints) debug = "Transaction Sequence: " + str(transaction_sequence) diff --git a/mythril/laser/ethereum/state/account.py b/mythril/laser/ethereum/state/account.py index 89a54586..0e51b524 100644 --- a/mythril/laser/ethereum/state/account.py +++ b/mythril/laser/ethereum/state/account.py @@ -1,6 +1,6 @@ from typing import Dict, Union, Any, KeysView -from z3 import BitVec, ExprRef +from z3 import BitVec, BitVecVal, ExprRef from mythril.disassembler.disassembly import Disassembly @@ -41,7 +41,7 @@ class Storage: pass if self.concrete: return 0 - self._storage[item] = BitVec("storage_" + str(item), 256) + self._storage[item] = BitVecVal(0, 256) return self._storage[item] def __setitem__(self, key: str, value: ExprRef) -> None: From 4ccc0d70e85a6f68c2bf2d78e086d7f6c5e50df7 Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Mon, 26 Nov 2018 18:28:33 +0700 Subject: [PATCH 05/41] Black formatting --- mythril/analysis/modules/ether_thief.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mythril/analysis/modules/ether_thief.py b/mythril/analysis/modules/ether_thief.py index 90a6d402..3b5c5697 100644 --- a/mythril/analysis/modules/ether_thief.py +++ b/mythril/analysis/modules/ether_thief.py @@ -66,7 +66,10 @@ class EtherThief(DetectionModule): constraints += [BVAddNoOverflow(eth_sent_total, tx.call_value, False)] eth_sent_total = Sum(eth_sent_total, tx.call_value) - constraints += [UGT(call_value, eth_sent_total), target == state.environment.sender] + constraints += [ + UGT(call_value, eth_sent_total), + target == state.environment.sender, + ] try: From d41b6e26769e23570c6b703d436f125067b93079 Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Mon, 26 Nov 2018 18:52:56 +0700 Subject: [PATCH 06/41] Update tests --- tests/cmd_line_test.py | 2 +- .../outputs_expected/calls.sol.o.json | 127 +----------------- .../outputs_expected/calls.sol.o.markdown | 60 --------- .../outputs_expected/calls.sol.o.text | 50 ------- .../outputs_expected/ether_send.sol.o.json | 31 +---- .../ether_send.sol.o.markdown | 26 +--- .../outputs_expected/ether_send.sol.o.text | 22 +-- .../outputs_expected/overflow.sol.o.json | 43 +----- .../outputs_expected/overflow.sol.o.markdown | 12 -- .../outputs_expected/overflow.sol.o.text | 11 -- .../outputs_expected/returnvalue.sol.o.json | 43 +----- .../returnvalue.sol.o.markdown | 24 ---- .../outputs_expected/returnvalue.sol.o.text | 20 --- .../outputs_expected/underflow.sol.o.json | 43 +----- .../outputs_expected/underflow.sol.o.markdown | 12 -- .../outputs_expected/underflow.sol.o.text | 11 -- 16 files changed, 9 insertions(+), 528 deletions(-) diff --git a/tests/cmd_line_test.py b/tests/cmd_line_test.py index ef846480..68640c2a 100644 --- a/tests/cmd_line_test.py +++ b/tests/cmd_line_test.py @@ -28,7 +28,7 @@ class CommandLineToolTestCase(BaseTestCase): class TruffleTestCase(BaseTestCase): def test_analysis_truffle_project(self): truffle_project_root = str(TESTS_DIR / "truffle_project") - command = "cd {}; truffle compile; python3 {} --truffle -t 1".format( + command = "cd {}; truffle compile; python3 {} --truffle -t 2".format( truffle_project_root, MYTH ) self.assertIn("=== Ether thief ====", output_of(command)) diff --git a/tests/testdata/outputs_expected/calls.sol.o.json b/tests/testdata/outputs_expected/calls.sol.o.json index c7a20444..874dd89d 100644 --- a/tests/testdata/outputs_expected/calls.sol.o.json +++ b/tests/testdata/outputs_expected/calls.sol.o.json @@ -1,126 +1 @@ -{ - "error": null, - "issues": [ - { - "address": 661, - "contract": "Unknown", - "debug": "", - "description": "This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code.", - "function": "thisisfine()", - "swc-id": "107", - "min_gas_used": 643, - "max_gas_used": 1254, - "title": "Message call to external contract", - "type": "Informational" - }, - { - "address": 666, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "thisisfine()", - "swc-id": "104", - "min_gas_used": 1352, - "max_gas_used": 35963, - "title": "Unchecked CALL return value", - "type": "Informational" - }, - { - "address": 779, - "contract": "Unknown", - "debug": "", - "description": "This contract executes a message call to an address found at storage slot 1. This storage slot can be written to by calling the function `setstoredaddress(address)`. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.", - "function": "callstoredaddress()", - "swc-id": "107", - "min_gas_used": 687, - "max_gas_used": 1298, - "title": "Message call to external contract", - "type": "Warning" - }, - { - "address": 779, - "contract": "Unknown", - "debug": "", - "description": "Possible transaction order dependence vulnerability: The value or direction of the call statement is determined from a tainted storage location.", - "function": "callstoredaddress()", - "swc-id": "114", - "min_gas_used": 687, - "max_gas_used": 1298, - "title": "Transaction order dependence", - "type": "Warning" - }, - { - "address": 784, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "callstoredaddress()", - "swc-id": "104", - "min_gas_used": 1396, - "max_gas_used": 36007, - "title": "Unchecked CALL return value", - "type": "Informational" - }, - { - "address": 858, - "contract": "Unknown", - "debug": "", - "description": "This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code.", - "function": "_function_0xe11f493e", - "swc-id": "107", - "min_gas_used": 709, - "max_gas_used": 1320, - "title": "Message call to external contract", - "type": "Informational" - }, - { - "address": 869, - "contract": "Unknown", - "debug": "", - "description": "The contract account state is changed after an external call. Consider that the called contract could re-enter the function before this state change takes place. This can lead to business logic vulnerabilities.", - "function": "_function_0xe11f493e", - "swc-id": "107", - "min_gas_used": 709, - "max_gas_used": 1320, - "title": "State change after external call", - "type": "Warning" - }, - { - "address": 871, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "_function_0xe11f493e", - "swc-id": "104", - "min_gas_used": 6432, - "max_gas_used": 61043, - "title": "Unchecked CALL return value", - "type": "Informational" - }, - { - "address": 912, - "contract": "Unknown", - "debug": "", - "description": "This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.", - "function": "calluseraddress(address)", - "swc-id": "107", - "min_gas_used": 335, - "max_gas_used": 616, - "title": "Message call to external contract", - "type": "Warning" - }, - { - "address": 918, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "calluseraddress(address)", - "swc-id": "104", - "min_gas_used": 1046, - "max_gas_used": 35327, - "title": "Unchecked CALL return value", - "type": "Informational" - } - ], - "success": true -} +{"error": null, "issues": [{"address": 666, "contract": "Unknown", "debug": "", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "thisisfine()", "max_gas_used": 35963, "min_gas_used": 1352, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}, {"address": 784, "contract": "Unknown", "debug": "", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "callstoredaddress()", "max_gas_used": 36007, "min_gas_used": 1396, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}, {"address": 871, "contract": "Unknown", "debug": "", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "_function_0xe11f493e", "max_gas_used": 61043, "min_gas_used": 6432, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}, {"address": 912, "contract": "Unknown", "debug": "", "description": "This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.", "function": "calluseraddress(address)", "max_gas_used": 616, "min_gas_used": 335, "swc-id": "107", "title": "Message call to external contract", "type": "Warning"}, {"address": 918, "contract": "Unknown", "debug": "", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "calluseraddress(address)", "max_gas_used": 35327, "min_gas_used": 1046, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}], "success": true} \ No newline at end of file diff --git a/tests/testdata/outputs_expected/calls.sol.o.markdown b/tests/testdata/outputs_expected/calls.sol.o.markdown index 5115c49b..297047d2 100644 --- a/tests/testdata/outputs_expected/calls.sol.o.markdown +++ b/tests/testdata/outputs_expected/calls.sol.o.markdown @@ -1,17 +1,5 @@ # Analysis results for test-filename.sol -## Message call to external contract -- SWC ID: 107 -- Type: Informational -- Contract: Unknown -- Function name: `thisisfine()` -- PC address: 661 -- Estimated Gas Usage: 643 - 1254 - -### Description - -This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code. - ## Unchecked CALL return value - SWC ID: 104 - Type: Informational @@ -24,30 +12,6 @@ This contract executes a message call to to another contract. Make sure that the The return value of an external call is not checked. Note that execution continue even if the called contract throws. -## Message call to external contract -- SWC ID: 107 -- Type: Warning -- Contract: Unknown -- Function name: `callstoredaddress()` -- PC address: 779 -- Estimated Gas Usage: 687 - 1298 - -### Description - -This contract executes a message call to an address found at storage slot 1. This storage slot can be written to by calling the function `setstoredaddress(address)`. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state. - -## Transaction order dependence -- SWC ID: 114 -- Type: Warning -- Contract: Unknown -- Function name: `callstoredaddress()` -- PC address: 779 -- Estimated Gas Usage: 687 - 1298 - -### Description - -Possible transaction order dependence vulnerability: The value or direction of the call statement is determined from a tainted storage location. - ## Unchecked CALL return value - SWC ID: 104 - Type: Informational @@ -60,30 +24,6 @@ Possible transaction order dependence vulnerability: The value or direction of t The return value of an external call is not checked. Note that execution continue even if the called contract throws. -## Message call to external contract -- SWC ID: 107 -- Type: Informational -- Contract: Unknown -- Function name: `_function_0xe11f493e` -- PC address: 858 -- Estimated Gas Usage: 709 - 1320 - -### Description - -This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code. - -## State change after external call -- SWC ID: 107 -- Type: Warning -- Contract: Unknown -- Function name: `_function_0xe11f493e` -- PC address: 869 -- Estimated Gas Usage: 709 - 1320 - -### Description - -The contract account state is changed after an external call. Consider that the called contract could re-enter the function before this state change takes place. This can lead to business logic vulnerabilities. - ## Unchecked CALL return value - SWC ID: 104 - Type: Informational diff --git a/tests/testdata/outputs_expected/calls.sol.o.text b/tests/testdata/outputs_expected/calls.sol.o.text index c3fd980c..02407bee 100644 --- a/tests/testdata/outputs_expected/calls.sol.o.text +++ b/tests/testdata/outputs_expected/calls.sol.o.text @@ -1,13 +1,3 @@ -==== Message call to external contract ==== -SWC ID: 107 -Type: Informational -Contract: Unknown -Function name: thisisfine() -PC address: 661 -Estimated Gas Usage: 643 - 1254 -This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code. --------------------- - ==== Unchecked CALL return value ==== SWC ID: 104 Type: Informational @@ -18,26 +8,6 @@ Estimated Gas Usage: 1352 - 35963 The return value of an external call is not checked. Note that execution continue even if the called contract throws. -------------------- -==== Message call to external contract ==== -SWC ID: 107 -Type: Warning -Contract: Unknown -Function name: callstoredaddress() -PC address: 779 -Estimated Gas Usage: 687 - 1298 -This contract executes a message call to an address found at storage slot 1. This storage slot can be written to by calling the function `setstoredaddress(address)`. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state. --------------------- - -==== Transaction order dependence ==== -SWC ID: 114 -Type: Warning -Contract: Unknown -Function name: callstoredaddress() -PC address: 779 -Estimated Gas Usage: 687 - 1298 -Possible transaction order dependence vulnerability: The value or direction of the call statement is determined from a tainted storage location. --------------------- - ==== Unchecked CALL return value ==== SWC ID: 104 Type: Informational @@ -48,26 +18,6 @@ Estimated Gas Usage: 1396 - 36007 The return value of an external call is not checked. Note that execution continue even if the called contract throws. -------------------- -==== Message call to external contract ==== -SWC ID: 107 -Type: Informational -Contract: Unknown -Function name: _function_0xe11f493e -PC address: 858 -Estimated Gas Usage: 709 - 1320 -This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code. --------------------- - -==== State change after external call ==== -SWC ID: 107 -Type: Warning -Contract: Unknown -Function name: _function_0xe11f493e -PC address: 869 -Estimated Gas Usage: 709 - 1320 -The contract account state is changed after an external call. Consider that the called contract could re-enter the function before this state change takes place. This can lead to business logic vulnerabilities. --------------------- - ==== Unchecked CALL return value ==== SWC ID: 104 Type: Informational diff --git a/tests/testdata/outputs_expected/ether_send.sol.o.json b/tests/testdata/outputs_expected/ether_send.sol.o.json index 1dc1cd5f..237b1c1e 100644 --- a/tests/testdata/outputs_expected/ether_send.sol.o.json +++ b/tests/testdata/outputs_expected/ether_send.sol.o.json @@ -1,30 +1 @@ -{ - "error": null, - "issues": [ - { - "address": 722, - "contract": "Unknown", - "debug": "", - "description": "Arbitrary senders other than the contract creator can withdraw ETH from the contract account without previously having sent an equivalent amount of ETH to it. This is likely to be a vulnerability.", - "function": "withdrawfunds()", - "max_gas_used": 1749, - "min_gas_used": 1138, - "swc-id": "105", - "title": "Ether thief", - "type": "Warning" - }, - { - "address": 883, - "contract": "Unknown", - "debug": "", - "description": "This binary add operation can result in integer overflow.\n", - "function": "invest()", - "max_gas_used": 1856, - "min_gas_used": 1571, - "swc-id": "101", - "title": "Integer Overflow", - "type": "Warning" - } - ], - "success": true -} +{"error": null, "issues": [], "success": true} \ No newline at end of file diff --git a/tests/testdata/outputs_expected/ether_send.sol.o.markdown b/tests/testdata/outputs_expected/ether_send.sol.o.markdown index c52ac9ec..321484fd 100644 --- a/tests/testdata/outputs_expected/ether_send.sol.o.markdown +++ b/tests/testdata/outputs_expected/ether_send.sol.o.markdown @@ -1,25 +1,3 @@ -# Analysis results for test-filename.sol +# Analysis results for None -## Ether thief -- SWC ID: 105 -- Type: Warning -- Contract: Unknown -- Function name: `withdrawfunds()` -- PC address: 722 -- Estimated Gas Usage: 1138 - 1749 - -### Description - -Arbitrary senders other than the contract creator can withdraw ETH from the contract account without previously having sent an equivalent amount of ETH to it. This is likely to be a vulnerability. - -## Integer Overflow -- SWC ID: 101 -- Type: Warning -- Contract: Unknown -- Function name: `invest()` -- PC address: 883 -- Estimated Gas Usage: 1571 - 1856 - -### Description - -This binary add operation can result in integer overflow. +The analysis was completed successfully. No issues were detected. diff --git a/tests/testdata/outputs_expected/ether_send.sol.o.text b/tests/testdata/outputs_expected/ether_send.sol.o.text index 0eebc17a..729320d8 100644 --- a/tests/testdata/outputs_expected/ether_send.sol.o.text +++ b/tests/testdata/outputs_expected/ether_send.sol.o.text @@ -1,21 +1 @@ -==== Ether thief ==== -SWC ID: 105 -Type: Warning -Contract: Unknown -Function name: withdrawfunds() -PC address: 722 -Estimated Gas Usage: 1138 - 1749 -Arbitrary senders other than the contract creator can withdraw ETH from the contract account without previously having sent an equivalent amount of ETH to it. This is likely to be a vulnerability. --------------------- - -==== Integer Overflow ==== -SWC ID: 101 -Type: Warning -Contract: Unknown -Function name: invest() -PC address: 883 -Estimated Gas Usage: 1571 - 1856 -This binary add operation can result in integer overflow. - --------------------- - +The analysis was completed successfully. No issues were detected. diff --git a/tests/testdata/outputs_expected/overflow.sol.o.json b/tests/testdata/outputs_expected/overflow.sol.o.json index 44a01d77..e1a4b9b3 100644 --- a/tests/testdata/outputs_expected/overflow.sol.o.json +++ b/tests/testdata/outputs_expected/overflow.sol.o.json @@ -1,42 +1 @@ -{ - "error": null, - "issues": [ - { - "address": 567, - "contract": "Unknown", - "debug": "", - "description": "The subtraction can result in an integer underflow.\n", - "function": "sendeth(address,uint256)", - "swc-id": "101", - "min_gas_used": 750, - "max_gas_used": 1035, - "title": "Integer Underflow", - "type": "Warning" - }, - { - "address": 649, - "contract": "Unknown", - "debug": "", - "description": "The subtraction can result in an integer underflow.\n", - "function": "sendeth(address,uint256)", - "swc-id": "101", - "min_gas_used": 1283, - "max_gas_used": 1758, - "title": "Integer Underflow", - "type": "Warning" - }, - { - "address": 725, - "contract": "Unknown", - "debug": "", - "description": "This binary add operation can result in integer overflow.\n", - "function": "sendeth(address,uint256)", - "swc-id": "101", - "min_gas_used": 6806, - "max_gas_used": 27471, - "title": "Integer Overflow", - "type": "Warning" - } - ], - "success": true -} +{"error": null, "issues": [{"address": 567, "contract": "Unknown", "debug": "", "description": "The subtraction can result in an integer underflow.\n", "function": "sendeth(address,uint256)", "max_gas_used": 1035, "min_gas_used": 750, "swc-id": "101", "title": "Integer Underflow", "type": "Warning"}, {"address": 649, "contract": "Unknown", "debug": "", "description": "The subtraction can result in an integer underflow.\n", "function": "sendeth(address,uint256)", "max_gas_used": 1758, "min_gas_used": 1283, "swc-id": "101", "title": "Integer Underflow", "type": "Warning"}], "success": true} \ No newline at end of file diff --git a/tests/testdata/outputs_expected/overflow.sol.o.markdown b/tests/testdata/outputs_expected/overflow.sol.o.markdown index b3043279..0e50eb96 100644 --- a/tests/testdata/outputs_expected/overflow.sol.o.markdown +++ b/tests/testdata/outputs_expected/overflow.sol.o.markdown @@ -23,15 +23,3 @@ The subtraction can result in an integer underflow. ### Description The subtraction can result in an integer underflow. - -## Integer Overflow -- SWC ID: 101 -- Type: Warning -- Contract: Unknown -- Function name: `sendeth(address,uint256)` -- PC address: 725 -- Estimated Gas Usage: 6806 - 27471 - -### Description - -This binary add operation can result in integer overflow. diff --git a/tests/testdata/outputs_expected/overflow.sol.o.text b/tests/testdata/outputs_expected/overflow.sol.o.text index 3e1b5154..3f13675f 100644 --- a/tests/testdata/outputs_expected/overflow.sol.o.text +++ b/tests/testdata/outputs_expected/overflow.sol.o.text @@ -20,14 +20,3 @@ The subtraction can result in an integer underflow. -------------------- -==== Integer Overflow ==== -SWC ID: 101 -Type: Warning -Contract: Unknown -Function name: sendeth(address,uint256) -PC address: 725 -Estimated Gas Usage: 6806 - 27471 -This binary add operation can result in integer overflow. - --------------------- - diff --git a/tests/testdata/outputs_expected/returnvalue.sol.o.json b/tests/testdata/outputs_expected/returnvalue.sol.o.json index ea39550b..8678a03f 100644 --- a/tests/testdata/outputs_expected/returnvalue.sol.o.json +++ b/tests/testdata/outputs_expected/returnvalue.sol.o.json @@ -1,42 +1 @@ -{ - "error": null, - "issues": [ - { - "address": 196, - "contract": "Unknown", - "debug": "", - "description": "This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code.", - "function": "callchecked()", - "swc-id": "107", - "min_gas_used": 599, - "max_gas_used": 1210, - "title": "Message call to external contract", - "type": "Informational" - }, - { - "address": 285, - "contract": "Unknown", - "debug": "", - "description": "This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code.", - "function": "callnotchecked()", - "swc-id": "107", - "min_gas_used": 621, - "max_gas_used": 1232, - "title": "Message call to external contract", - "type": "Informational" - }, - { - "address": 290, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "callnotchecked()", - "swc-id": "104", - "min_gas_used": 1330, - "max_gas_used": 35941, - "title": "Unchecked CALL return value", - "type": "Informational" - } - ], - "success": true -} +{"error": null, "issues": [{"address": 290, "contract": "Unknown", "debug": "", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "callnotchecked()", "max_gas_used": 35941, "min_gas_used": 1330, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}], "success": true} \ No newline at end of file diff --git a/tests/testdata/outputs_expected/returnvalue.sol.o.markdown b/tests/testdata/outputs_expected/returnvalue.sol.o.markdown index ebc6fcee..652c22e6 100644 --- a/tests/testdata/outputs_expected/returnvalue.sol.o.markdown +++ b/tests/testdata/outputs_expected/returnvalue.sol.o.markdown @@ -1,29 +1,5 @@ # Analysis results for test-filename.sol -## Message call to external contract -- SWC ID: 107 -- Type: Informational -- Contract: Unknown -- Function name: `callchecked()` -- PC address: 196 -- Estimated Gas Usage: 599 - 1210 - -### Description - -This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code. - -## Message call to external contract -- SWC ID: 107 -- Type: Informational -- Contract: Unknown -- Function name: `callnotchecked()` -- PC address: 285 -- Estimated Gas Usage: 621 - 1232 - -### Description - -This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code. - ## Unchecked CALL return value - SWC ID: 104 - Type: Informational diff --git a/tests/testdata/outputs_expected/returnvalue.sol.o.text b/tests/testdata/outputs_expected/returnvalue.sol.o.text index c8d7c1bf..98898844 100644 --- a/tests/testdata/outputs_expected/returnvalue.sol.o.text +++ b/tests/testdata/outputs_expected/returnvalue.sol.o.text @@ -1,23 +1,3 @@ -==== Message call to external contract ==== -SWC ID: 107 -Type: Informational -Contract: Unknown -Function name: callchecked() -PC address: 196 -Estimated Gas Usage: 599 - 1210 -This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code. --------------------- - -==== Message call to external contract ==== -SWC ID: 107 -Type: Informational -Contract: Unknown -Function name: callnotchecked() -PC address: 285 -Estimated Gas Usage: 621 - 1232 -This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code. --------------------- - ==== Unchecked CALL return value ==== SWC ID: 104 Type: Informational diff --git a/tests/testdata/outputs_expected/underflow.sol.o.json b/tests/testdata/outputs_expected/underflow.sol.o.json index 44a01d77..e1a4b9b3 100644 --- a/tests/testdata/outputs_expected/underflow.sol.o.json +++ b/tests/testdata/outputs_expected/underflow.sol.o.json @@ -1,42 +1 @@ -{ - "error": null, - "issues": [ - { - "address": 567, - "contract": "Unknown", - "debug": "", - "description": "The subtraction can result in an integer underflow.\n", - "function": "sendeth(address,uint256)", - "swc-id": "101", - "min_gas_used": 750, - "max_gas_used": 1035, - "title": "Integer Underflow", - "type": "Warning" - }, - { - "address": 649, - "contract": "Unknown", - "debug": "", - "description": "The subtraction can result in an integer underflow.\n", - "function": "sendeth(address,uint256)", - "swc-id": "101", - "min_gas_used": 1283, - "max_gas_used": 1758, - "title": "Integer Underflow", - "type": "Warning" - }, - { - "address": 725, - "contract": "Unknown", - "debug": "", - "description": "This binary add operation can result in integer overflow.\n", - "function": "sendeth(address,uint256)", - "swc-id": "101", - "min_gas_used": 6806, - "max_gas_used": 27471, - "title": "Integer Overflow", - "type": "Warning" - } - ], - "success": true -} +{"error": null, "issues": [{"address": 567, "contract": "Unknown", "debug": "", "description": "The subtraction can result in an integer underflow.\n", "function": "sendeth(address,uint256)", "max_gas_used": 1035, "min_gas_used": 750, "swc-id": "101", "title": "Integer Underflow", "type": "Warning"}, {"address": 649, "contract": "Unknown", "debug": "", "description": "The subtraction can result in an integer underflow.\n", "function": "sendeth(address,uint256)", "max_gas_used": 1758, "min_gas_used": 1283, "swc-id": "101", "title": "Integer Underflow", "type": "Warning"}], "success": true} \ No newline at end of file diff --git a/tests/testdata/outputs_expected/underflow.sol.o.markdown b/tests/testdata/outputs_expected/underflow.sol.o.markdown index b3043279..0e50eb96 100644 --- a/tests/testdata/outputs_expected/underflow.sol.o.markdown +++ b/tests/testdata/outputs_expected/underflow.sol.o.markdown @@ -23,15 +23,3 @@ The subtraction can result in an integer underflow. ### Description The subtraction can result in an integer underflow. - -## Integer Overflow -- SWC ID: 101 -- Type: Warning -- Contract: Unknown -- Function name: `sendeth(address,uint256)` -- PC address: 725 -- Estimated Gas Usage: 6806 - 27471 - -### Description - -This binary add operation can result in integer overflow. diff --git a/tests/testdata/outputs_expected/underflow.sol.o.text b/tests/testdata/outputs_expected/underflow.sol.o.text index 3e1b5154..3f13675f 100644 --- a/tests/testdata/outputs_expected/underflow.sol.o.text +++ b/tests/testdata/outputs_expected/underflow.sol.o.text @@ -20,14 +20,3 @@ The subtraction can result in an integer underflow. -------------------- -==== Integer Overflow ==== -SWC ID: 101 -Type: Warning -Contract: Unknown -Function name: sendeth(address,uint256) -PC address: 725 -Estimated Gas Usage: 6806 - 27471 -This binary add operation can result in integer overflow. - --------------------- - From 2f24a16cc18948516c4f2f384078174d20ef70b5 Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Mon, 26 Nov 2018 19:09:57 +0700 Subject: [PATCH 07/41] Remove the pesky config.ini --- tests/mythril_dir/config.ini | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 tests/mythril_dir/config.ini diff --git a/tests/mythril_dir/config.ini b/tests/mythril_dir/config.ini deleted file mode 100644 index 71bab4ea..00000000 --- a/tests/mythril_dir/config.ini +++ /dev/null @@ -1,4 +0,0 @@ -[defaults] -leveldb_dir = /Users/bernhardmueller/Library/Ethereum/geth/chaindata -dynamic_loading = infura - From 7cb6e0ecf41191fe6ba053deb92aeaa1697e0057 Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Mon, 26 Nov 2018 22:06:30 +0700 Subject: [PATCH 08/41] Add missing BitVecVal() --- mythril/laser/ethereum/transaction/symbolic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mythril/laser/ethereum/transaction/symbolic.py b/mythril/laser/ethereum/transaction/symbolic.py index 6d58d195..fbfbf347 100644 --- a/mythril/laser/ethereum/transaction/symbolic.py +++ b/mythril/laser/ethereum/transaction/symbolic.py @@ -68,7 +68,7 @@ def execute_contract_creation( gas_limit=8000000, # block gas limit origin=BitVec("origin{}".format(next_transaction_id), 256), code=Disassembly(contract_initialization_code), - caller=CREATOR_ADDRESS, + caller=BitVecVal(CREATOR_ADDRESS, 256), callee_account=new_account, call_data=[], call_data_type=CalldataType.SYMBOLIC, From 0ed27369c7d08e837723834324cde4e3ae3dc66f Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Mon, 26 Nov 2018 22:21:54 +0700 Subject: [PATCH 09/41] Pretty-print tests --- .../outputs_expected/calls.sol.o.json | 67 +++- .../outputs_expected/environments.sol.o.json | 68 ++-- .../outputs_expected/ether_send.sol.o.json | 2 +- .../outputs_expected/exceptions.sol.o.json | 104 +++--- .../kinds_of_calls.sol.o.json | 104 +++--- .../multi_contracts.sol.o.json | 32 +- .../outputs_expected/nonascii.sol.o.json | 6 +- .../outputs_expected/origin.sol.o.json | 32 +- .../outputs_expected/overflow.sol.o.json | 31 +- .../outputs_expected/returnvalue.sol.o.json | 19 +- .../outputs_expected/rubixi.sol.o.json | 328 +++++++++--------- .../outputs_expected/suicide.sol.o.json | 19 +- .../outputs_expected/underflow.sol.o.json | 31 +- .../outputs_expected/weak_random.sol.o.json | 88 ++--- 14 files changed, 542 insertions(+), 389 deletions(-) diff --git a/tests/testdata/outputs_expected/calls.sol.o.json b/tests/testdata/outputs_expected/calls.sol.o.json index 874dd89d..1856cba1 100644 --- a/tests/testdata/outputs_expected/calls.sol.o.json +++ b/tests/testdata/outputs_expected/calls.sol.o.json @@ -1 +1,66 @@ -{"error": null, "issues": [{"address": 666, "contract": "Unknown", "debug": "", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "thisisfine()", "max_gas_used": 35963, "min_gas_used": 1352, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}, {"address": 784, "contract": "Unknown", "debug": "", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "callstoredaddress()", "max_gas_used": 36007, "min_gas_used": 1396, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}, {"address": 871, "contract": "Unknown", "debug": "", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "_function_0xe11f493e", "max_gas_used": 61043, "min_gas_used": 6432, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}, {"address": 912, "contract": "Unknown", "debug": "", "description": "This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.", "function": "calluseraddress(address)", "max_gas_used": 616, "min_gas_used": 335, "swc-id": "107", "title": "Message call to external contract", "type": "Warning"}, {"address": 918, "contract": "Unknown", "debug": "", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "calluseraddress(address)", "max_gas_used": 35327, "min_gas_used": 1046, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}], "success": true} \ No newline at end of file +{ + "error": null, + "issues": [ + { + "address": 666, + "contract": "Unknown", + "debug": "", + "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", + "function": "thisisfine()", + "max_gas_used": 35963, + "min_gas_used": 1352, + "swc-id": "104", + "title": "Unchecked CALL return value", + "type": "Informational", + }, + { + "address": 784, + "contract": "Unknown", + "debug": "", + "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", + "function": "callstoredaddress()", + "max_gas_used": 36007, + "min_gas_used": 1396, + "swc-id": "104", + "title": "Unchecked CALL return value", + "type": "Informational", + }, + { + "address": 871, + "contract": "Unknown", + "debug": "", + "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", + "function": "_function_0xe11f493e", + "max_gas_used": 61043, + "min_gas_used": 6432, + "swc-id": "104", + "title": "Unchecked CALL return value", + "type": "Informational", + }, + { + "address": 912, + "contract": "Unknown", + "debug": "", + "description": "This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.", + "function": "calluseraddress(address)", + "max_gas_used": 616, + "min_gas_used": 335, + "swc-id": "107", + "title": "Message call to external contract", + "type": "Warning", + }, + { + "address": 918, + "contract": "Unknown", + "debug": "", + "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", + "function": "calluseraddress(address)", + "max_gas_used": 35327, + "min_gas_used": 1046, + "swc-id": "104", + "title": "Unchecked CALL return value", + "type": "Informational", + }, + ], + "success": true, +} diff --git a/tests/testdata/outputs_expected/environments.sol.o.json b/tests/testdata/outputs_expected/environments.sol.o.json index c5a4b1cd..5cd8f4db 100644 --- a/tests/testdata/outputs_expected/environments.sol.o.json +++ b/tests/testdata/outputs_expected/environments.sol.o.json @@ -1,36 +1,36 @@ { - "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 + "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, } diff --git a/tests/testdata/outputs_expected/ether_send.sol.o.json b/tests/testdata/outputs_expected/ether_send.sol.o.json index 237b1c1e..84a01045 100644 --- a/tests/testdata/outputs_expected/ether_send.sol.o.json +++ b/tests/testdata/outputs_expected/ether_send.sol.o.json @@ -1 +1 @@ -{"error": null, "issues": [], "success": true} \ No newline at end of file +{"error": null, "issues": [], "success": true} diff --git a/tests/testdata/outputs_expected/exceptions.sol.o.json b/tests/testdata/outputs_expected/exceptions.sol.o.json index 35e82238..639bad6c 100644 --- a/tests/testdata/outputs_expected/exceptions.sol.o.json +++ b/tests/testdata/outputs_expected/exceptions.sol.o.json @@ -1,54 +1,54 @@ { - "error": null, - "issues": [ - { - "address": 446, - "contract": "Unknown", - "debug": "", - "description": "A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking.", - "function": "assert3(uint256)", - "swc-id": "110", - "min_gas_used": 206, - "max_gas_used": 301, - "title": "Exception state", - "type": "Informational" - }, - { - "address": 484, - "contract": "Unknown", - "debug": "", - "description": "A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking.", - "function": "arrayaccess(uint256)", - "swc-id": "110", - "min_gas_used": 256, - "max_gas_used": 351, - "title": "Exception state", - "type": "Informational" - }, - { - "address": 506, - "contract": "Unknown", - "debug": "", - "description": "A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking.", - "function": "divisionby0(uint256)", - "swc-id": "110", - "min_gas_used": 272, - "max_gas_used": 367, - "title": "Exception state", - "type": "Informational" - }, - { - "address": 531, - "contract": "Unknown", - "debug": "", - "description": "A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking.", - "function": "assert1()", - "swc-id": "110", - "min_gas_used": 268, - "max_gas_used": 363, - "title": "Exception state", - "type": "Informational" - } - ], - "success": true + "error": null, + "issues": [ + { + "address": 446, + "contract": "Unknown", + "debug": "", + "description": "A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking.", + "function": "assert3(uint256)", + "swc-id": "110", + "min_gas_used": 206, + "max_gas_used": 301, + "title": "Exception state", + "type": "Informational", + }, + { + "address": 484, + "contract": "Unknown", + "debug": "", + "description": "A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking.", + "function": "arrayaccess(uint256)", + "swc-id": "110", + "min_gas_used": 256, + "max_gas_used": 351, + "title": "Exception state", + "type": "Informational", + }, + { + "address": 506, + "contract": "Unknown", + "debug": "", + "description": "A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking.", + "function": "divisionby0(uint256)", + "swc-id": "110", + "min_gas_used": 272, + "max_gas_used": 367, + "title": "Exception state", + "type": "Informational", + }, + { + "address": 531, + "contract": "Unknown", + "debug": "", + "description": "A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking.", + "function": "assert1()", + "swc-id": "110", + "min_gas_used": 268, + "max_gas_used": 363, + "title": "Exception state", + "type": "Informational", + }, + ], + "success": true, } diff --git a/tests/testdata/outputs_expected/kinds_of_calls.sol.o.json b/tests/testdata/outputs_expected/kinds_of_calls.sol.o.json index 3674dde9..d6372b4c 100644 --- a/tests/testdata/outputs_expected/kinds_of_calls.sol.o.json +++ b/tests/testdata/outputs_expected/kinds_of_calls.sol.o.json @@ -1,54 +1,54 @@ { - "error": null, - "issues": [ - { - "address": 626, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "_function_0x141f32ff", - "swc-id": "104", - "min_gas_used": 1104, - "max_gas_used": 35856, - "title": "Unchecked CALL return value", - "type": "Informational" - }, - { - "address": 857, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "_function_0x9b58bc26", - "swc-id": "104", - "min_gas_used": 1167, - "max_gas_used": 35919, - "title": "Unchecked CALL return value", - "type": "Informational" - }, - { - "address": 1038, - "contract": "Unknown", - "debug": "", - "description": "This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.", - "function": "_function_0xeea4c864", - "swc-id": "107", - "min_gas_used": 477, - "max_gas_used": 1229, - "title": "Message call to external contract", - "type": "Warning" - }, - { - "address": 1046, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "_function_0xeea4c864", - "swc-id": "104", - "min_gas_used": 1192, - "max_gas_used": 35944, - "title": "Unchecked CALL return value", - "type": "Informational" - } - ], - "success": true + "error": null, + "issues": [ + { + "address": 626, + "contract": "Unknown", + "debug": "", + "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", + "function": "_function_0x141f32ff", + "swc-id": "104", + "min_gas_used": 1104, + "max_gas_used": 35856, + "title": "Unchecked CALL return value", + "type": "Informational", + }, + { + "address": 857, + "contract": "Unknown", + "debug": "", + "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", + "function": "_function_0x9b58bc26", + "swc-id": "104", + "min_gas_used": 1167, + "max_gas_used": 35919, + "title": "Unchecked CALL return value", + "type": "Informational", + }, + { + "address": 1038, + "contract": "Unknown", + "debug": "", + "description": "This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.", + "function": "_function_0xeea4c864", + "swc-id": "107", + "min_gas_used": 477, + "max_gas_used": 1229, + "title": "Message call to external contract", + "type": "Warning", + }, + { + "address": 1046, + "contract": "Unknown", + "debug": "", + "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", + "function": "_function_0xeea4c864", + "swc-id": "104", + "min_gas_used": 1192, + "max_gas_used": 35944, + "title": "Unchecked CALL return value", + "type": "Informational", + }, + ], + "success": true, } diff --git a/tests/testdata/outputs_expected/multi_contracts.sol.o.json b/tests/testdata/outputs_expected/multi_contracts.sol.o.json index c4d6e54c..891f0203 100644 --- a/tests/testdata/outputs_expected/multi_contracts.sol.o.json +++ b/tests/testdata/outputs_expected/multi_contracts.sol.o.json @@ -1,18 +1,18 @@ { - "error": null, - "issues": [ - { - "address": 142, - "contract": "Unknown", - "debug": "", - "description": "Arbitrary senders other than the contract creator can withdraw ETH from the contract account without previously having sent an equivalent amount of ETH to it. This is likely to be a vulnerability.", - "function": "transfer()", - "max_gas_used": 467, - "min_gas_used": 186, - "swc-id": "105", - "title": "Ether thief", - "type": "Warning" - } - ], - "success": true + "error": null, + "issues": [ + { + "address": 142, + "contract": "Unknown", + "debug": "", + "description": "Arbitrary senders other than the contract creator can withdraw ETH from the contract account without previously having sent an equivalent amount of ETH to it. This is likely to be a vulnerability.", + "function": "transfer()", + "max_gas_used": 467, + "min_gas_used": 186, + "swc-id": "105", + "title": "Ether thief", + "type": "Warning", + } + ], + "success": true, } diff --git a/tests/testdata/outputs_expected/nonascii.sol.o.json b/tests/testdata/outputs_expected/nonascii.sol.o.json index 179ed787..84a01045 100644 --- a/tests/testdata/outputs_expected/nonascii.sol.o.json +++ b/tests/testdata/outputs_expected/nonascii.sol.o.json @@ -1,5 +1 @@ -{ - "error": null, - "issues": [], - "success": true -} +{"error": null, "issues": [], "success": true} diff --git a/tests/testdata/outputs_expected/origin.sol.o.json b/tests/testdata/outputs_expected/origin.sol.o.json index bcfd5e84..4f756563 100644 --- a/tests/testdata/outputs_expected/origin.sol.o.json +++ b/tests/testdata/outputs_expected/origin.sol.o.json @@ -1,18 +1,18 @@ { - "error": null, - "issues": [ - { - "address": 317, - "contract": "Unknown", - "debug": "", - "description": "The function `transferOwnership(address)` retrieves the transaction origin (tx.origin) using the ORIGIN opcode. Use msg.sender instead.\nSee also: https://solidity.readthedocs.io/en/develop/security-considerations.html#tx-origin", - "function": "transferOwnership(address)", - "swc-id": "115", - "min_gas_used": 626, - "max_gas_used": 1051, - "title": "Use of tx.origin", - "type": "Warning" - } - ], - "success": true + "error": null, + "issues": [ + { + "address": 317, + "contract": "Unknown", + "debug": "", + "description": "The function `transferOwnership(address)` retrieves the transaction origin (tx.origin) using the ORIGIN opcode. Use msg.sender instead.\nSee also: https://solidity.readthedocs.io/en/develop/security-considerations.html#tx-origin", + "function": "transferOwnership(address)", + "swc-id": "115", + "min_gas_used": 626, + "max_gas_used": 1051, + "title": "Use of tx.origin", + "type": "Warning", + } + ], + "success": true, } diff --git a/tests/testdata/outputs_expected/overflow.sol.o.json b/tests/testdata/outputs_expected/overflow.sol.o.json index e1a4b9b3..a6a780cb 100644 --- a/tests/testdata/outputs_expected/overflow.sol.o.json +++ b/tests/testdata/outputs_expected/overflow.sol.o.json @@ -1 +1,30 @@ -{"error": null, "issues": [{"address": 567, "contract": "Unknown", "debug": "", "description": "The subtraction can result in an integer underflow.\n", "function": "sendeth(address,uint256)", "max_gas_used": 1035, "min_gas_used": 750, "swc-id": "101", "title": "Integer Underflow", "type": "Warning"}, {"address": 649, "contract": "Unknown", "debug": "", "description": "The subtraction can result in an integer underflow.\n", "function": "sendeth(address,uint256)", "max_gas_used": 1758, "min_gas_used": 1283, "swc-id": "101", "title": "Integer Underflow", "type": "Warning"}], "success": true} \ No newline at end of file +{ + "error": null, + "issues": [ + { + "address": 567, + "contract": "Unknown", + "debug": "", + "description": "The subtraction can result in an integer underflow.\n", + "function": "sendeth(address,uint256)", + "max_gas_used": 1035, + "min_gas_used": 750, + "swc-id": "101", + "title": "Integer Underflow", + "type": "Warning", + }, + { + "address": 649, + "contract": "Unknown", + "debug": "", + "description": "The subtraction can result in an integer underflow.\n", + "function": "sendeth(address,uint256)", + "max_gas_used": 1758, + "min_gas_used": 1283, + "swc-id": "101", + "title": "Integer Underflow", + "type": "Warning", + }, + ], + "success": true, +} diff --git a/tests/testdata/outputs_expected/returnvalue.sol.o.json b/tests/testdata/outputs_expected/returnvalue.sol.o.json index 8678a03f..f6be6a1e 100644 --- a/tests/testdata/outputs_expected/returnvalue.sol.o.json +++ b/tests/testdata/outputs_expected/returnvalue.sol.o.json @@ -1 +1,18 @@ -{"error": null, "issues": [{"address": 290, "contract": "Unknown", "debug": "", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "callnotchecked()", "max_gas_used": 35941, "min_gas_used": 1330, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}], "success": true} \ No newline at end of file +{ + "error": null, + "issues": [ + { + "address": 290, + "contract": "Unknown", + "debug": "", + "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", + "function": "callnotchecked()", + "max_gas_used": 35941, + "min_gas_used": 1330, + "swc-id": "104", + "title": "Unchecked CALL return value", + "type": "Informational", + } + ], + "success": true, +} diff --git a/tests/testdata/outputs_expected/rubixi.sol.o.json b/tests/testdata/outputs_expected/rubixi.sol.o.json index a135559e..bac935c7 100644 --- a/tests/testdata/outputs_expected/rubixi.sol.o.json +++ b/tests/testdata/outputs_expected/rubixi.sol.o.json @@ -1,166 +1,166 @@ { - "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": "" - } - ] + "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": "", + }, + ], } diff --git a/tests/testdata/outputs_expected/suicide.sol.o.json b/tests/testdata/outputs_expected/suicide.sol.o.json index ebcb11d2..ca43c488 100644 --- a/tests/testdata/outputs_expected/suicide.sol.o.json +++ b/tests/testdata/outputs_expected/suicide.sol.o.json @@ -1 +1,18 @@ -{"error": null, "issues": [{"address": 146, "contract": "Unknown", "debug": "", "description": "Anyone can kill this contract and withdraw its balance to their own account.", "function": "kill(address)", "max_gas_used": 263, "min_gas_used": 168, "swc-id": "106", "title": "Unchecked SUICIDE", "type": "Warning"}], "success": true} \ No newline at end of file +{ + "error": null, + "issues": [ + { + "address": 146, + "contract": "Unknown", + "debug": "", + "description": "Anyone can kill this contract and withdraw its balance to their own account.", + "function": "kill(address)", + "max_gas_used": 263, + "min_gas_used": 168, + "swc-id": "106", + "title": "Unchecked SUICIDE", + "type": "Warning", + } + ], + "success": true, +} diff --git a/tests/testdata/outputs_expected/underflow.sol.o.json b/tests/testdata/outputs_expected/underflow.sol.o.json index e1a4b9b3..a6a780cb 100644 --- a/tests/testdata/outputs_expected/underflow.sol.o.json +++ b/tests/testdata/outputs_expected/underflow.sol.o.json @@ -1 +1,30 @@ -{"error": null, "issues": [{"address": 567, "contract": "Unknown", "debug": "", "description": "The subtraction can result in an integer underflow.\n", "function": "sendeth(address,uint256)", "max_gas_used": 1035, "min_gas_used": 750, "swc-id": "101", "title": "Integer Underflow", "type": "Warning"}, {"address": 649, "contract": "Unknown", "debug": "", "description": "The subtraction can result in an integer underflow.\n", "function": "sendeth(address,uint256)", "max_gas_used": 1758, "min_gas_used": 1283, "swc-id": "101", "title": "Integer Underflow", "type": "Warning"}], "success": true} \ No newline at end of file +{ + "error": null, + "issues": [ + { + "address": 567, + "contract": "Unknown", + "debug": "", + "description": "The subtraction can result in an integer underflow.\n", + "function": "sendeth(address,uint256)", + "max_gas_used": 1035, + "min_gas_used": 750, + "swc-id": "101", + "title": "Integer Underflow", + "type": "Warning", + }, + { + "address": 649, + "contract": "Unknown", + "debug": "", + "description": "The subtraction can result in an integer underflow.\n", + "function": "sendeth(address,uint256)", + "max_gas_used": 1758, + "min_gas_used": 1283, + "swc-id": "101", + "title": "Integer Underflow", + "type": "Warning", + }, + ], + "success": true, +} diff --git a/tests/testdata/outputs_expected/weak_random.sol.o.json b/tests/testdata/outputs_expected/weak_random.sol.o.json index 630fdb0d..681f8544 100644 --- a/tests/testdata/outputs_expected/weak_random.sol.o.json +++ b/tests/testdata/outputs_expected/weak_random.sol.o.json @@ -1,46 +1,46 @@ { - "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": "" - } - ] + "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": "", + }, + ], } From 408686e739680cabebbd936490f6c9db929c9b14 Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Mon, 26 Nov 2018 22:51:24 +0700 Subject: [PATCH 10/41] Fix test files formatting --- .../outputs_expected/calls.sol.o.json | 128 +++++++++--------- .../outputs_expected/ether_send.sol.o.json | 6 +- .../outputs_expected/exceptions.sol.o.json | 104 +++++++------- .../kinds_of_calls.sol.o.json | 104 +++++++------- .../outputs_expected/metacoin.sol.o.json | 1 + .../outputs_expected/metacoin.sol.o.markdown | 13 ++ .../outputs_expected/metacoin.sol.o.text | 11 ++ .../multi_contracts.sol.o.json | 32 ++--- .../outputs_expected/nonascii.sol.o.json | 6 +- .../outputs_expected/origin.sol.o.json | 32 ++--- .../outputs_expected/overflow.sol.o.json | 56 ++++---- .../outputs_expected/returnvalue.sol.o.json | 32 ++--- .../outputs_expected/suicide.sol.o.json | 32 ++--- .../outputs_expected/underflow.sol.o.json | 56 ++++---- 14 files changed, 323 insertions(+), 290 deletions(-) create mode 100644 tests/testdata/outputs_expected/metacoin.sol.o.json create mode 100644 tests/testdata/outputs_expected/metacoin.sol.o.markdown create mode 100644 tests/testdata/outputs_expected/metacoin.sol.o.text diff --git a/tests/testdata/outputs_expected/calls.sol.o.json b/tests/testdata/outputs_expected/calls.sol.o.json index 1856cba1..6d4d8063 100644 --- a/tests/testdata/outputs_expected/calls.sol.o.json +++ b/tests/testdata/outputs_expected/calls.sol.o.json @@ -1,66 +1,66 @@ { - "error": null, - "issues": [ - { - "address": 666, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "thisisfine()", - "max_gas_used": 35963, - "min_gas_used": 1352, - "swc-id": "104", - "title": "Unchecked CALL return value", - "type": "Informational", - }, - { - "address": 784, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "callstoredaddress()", - "max_gas_used": 36007, - "min_gas_used": 1396, - "swc-id": "104", - "title": "Unchecked CALL return value", - "type": "Informational", - }, - { - "address": 871, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "_function_0xe11f493e", - "max_gas_used": 61043, - "min_gas_used": 6432, - "swc-id": "104", - "title": "Unchecked CALL return value", - "type": "Informational", - }, - { - "address": 912, - "contract": "Unknown", - "debug": "", - "description": "This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.", - "function": "calluseraddress(address)", - "max_gas_used": 616, - "min_gas_used": 335, - "swc-id": "107", - "title": "Message call to external contract", - "type": "Warning", - }, - { - "address": 918, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "calluseraddress(address)", - "max_gas_used": 35327, - "min_gas_used": 1046, - "swc-id": "104", - "title": "Unchecked CALL return value", - "type": "Informational", - }, - ], - "success": true, + "error": null, + "issues": [ + { + "address": 666, + "contract": "Unknown", + "debug": "", + "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", + "function": "thisisfine()", + "max_gas_used": 35963, + "min_gas_used": 1352, + "swc-id": "104", + "title": "Unchecked CALL return value", + "type": "Informational" + }, + { + "address": 784, + "contract": "Unknown", + "debug": "", + "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", + "function": "callstoredaddress()", + "max_gas_used": 36007, + "min_gas_used": 1396, + "swc-id": "104", + "title": "Unchecked CALL return value", + "type": "Informational" + }, + { + "address": 871, + "contract": "Unknown", + "debug": "", + "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", + "function": "_function_0xe11f493e", + "max_gas_used": 61043, + "min_gas_used": 6432, + "swc-id": "104", + "title": "Unchecked CALL return value", + "type": "Informational" + }, + { + "address": 912, + "contract": "Unknown", + "debug": "", + "description": "This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.", + "function": "calluseraddress(address)", + "max_gas_used": 616, + "min_gas_used": 335, + "swc-id": "107", + "title": "Message call to external contract", + "type": "Warning" + }, + { + "address": 918, + "contract": "Unknown", + "debug": "", + "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", + "function": "calluseraddress(address)", + "max_gas_used": 35327, + "min_gas_used": 1046, + "swc-id": "104", + "title": "Unchecked CALL return value", + "type": "Informational" + } + ], + "success": true } diff --git a/tests/testdata/outputs_expected/ether_send.sol.o.json b/tests/testdata/outputs_expected/ether_send.sol.o.json index 84a01045..179ed787 100644 --- a/tests/testdata/outputs_expected/ether_send.sol.o.json +++ b/tests/testdata/outputs_expected/ether_send.sol.o.json @@ -1 +1,5 @@ -{"error": null, "issues": [], "success": true} +{ + "error": null, + "issues": [], + "success": true +} diff --git a/tests/testdata/outputs_expected/exceptions.sol.o.json b/tests/testdata/outputs_expected/exceptions.sol.o.json index 639bad6c..c0324b17 100644 --- a/tests/testdata/outputs_expected/exceptions.sol.o.json +++ b/tests/testdata/outputs_expected/exceptions.sol.o.json @@ -1,54 +1,54 @@ { - "error": null, - "issues": [ - { - "address": 446, - "contract": "Unknown", - "debug": "", - "description": "A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking.", - "function": "assert3(uint256)", - "swc-id": "110", - "min_gas_used": 206, - "max_gas_used": 301, - "title": "Exception state", - "type": "Informational", - }, - { - "address": 484, - "contract": "Unknown", - "debug": "", - "description": "A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking.", - "function": "arrayaccess(uint256)", - "swc-id": "110", - "min_gas_used": 256, - "max_gas_used": 351, - "title": "Exception state", - "type": "Informational", - }, - { - "address": 506, - "contract": "Unknown", - "debug": "", - "description": "A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking.", - "function": "divisionby0(uint256)", - "swc-id": "110", - "min_gas_used": 272, - "max_gas_used": 367, - "title": "Exception state", - "type": "Informational", - }, - { - "address": 531, - "contract": "Unknown", - "debug": "", - "description": "A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking.", - "function": "assert1()", - "swc-id": "110", - "min_gas_used": 268, - "max_gas_used": 363, - "title": "Exception state", - "type": "Informational", - }, - ], - "success": true, + "error": null, + "issues": [ + { + "address": 446, + "contract": "Unknown", + "debug": "", + "description": "A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking.", + "function": "assert3(uint256)", + "max_gas_used": 301, + "min_gas_used": 206, + "swc-id": "110", + "title": "Exception state", + "type": "Informational" + }, + { + "address": 484, + "contract": "Unknown", + "debug": "", + "description": "A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking.", + "function": "arrayaccess(uint256)", + "max_gas_used": 351, + "min_gas_used": 256, + "swc-id": "110", + "title": "Exception state", + "type": "Informational" + }, + { + "address": 506, + "contract": "Unknown", + "debug": "", + "description": "A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking.", + "function": "divisionby0(uint256)", + "max_gas_used": 367, + "min_gas_used": 272, + "swc-id": "110", + "title": "Exception state", + "type": "Informational" + }, + { + "address": 531, + "contract": "Unknown", + "debug": "", + "description": "A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking.", + "function": "assert1()", + "max_gas_used": 363, + "min_gas_used": 268, + "swc-id": "110", + "title": "Exception state", + "type": "Informational" + } + ], + "success": true } diff --git a/tests/testdata/outputs_expected/kinds_of_calls.sol.o.json b/tests/testdata/outputs_expected/kinds_of_calls.sol.o.json index d6372b4c..a373f020 100644 --- a/tests/testdata/outputs_expected/kinds_of_calls.sol.o.json +++ b/tests/testdata/outputs_expected/kinds_of_calls.sol.o.json @@ -1,54 +1,54 @@ { - "error": null, - "issues": [ - { - "address": 626, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "_function_0x141f32ff", - "swc-id": "104", - "min_gas_used": 1104, - "max_gas_used": 35856, - "title": "Unchecked CALL return value", - "type": "Informational", - }, - { - "address": 857, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "_function_0x9b58bc26", - "swc-id": "104", - "min_gas_used": 1167, - "max_gas_used": 35919, - "title": "Unchecked CALL return value", - "type": "Informational", - }, - { - "address": 1038, - "contract": "Unknown", - "debug": "", - "description": "This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.", - "function": "_function_0xeea4c864", - "swc-id": "107", - "min_gas_used": 477, - "max_gas_used": 1229, - "title": "Message call to external contract", - "type": "Warning", - }, - { - "address": 1046, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "_function_0xeea4c864", - "swc-id": "104", - "min_gas_used": 1192, - "max_gas_used": 35944, - "title": "Unchecked CALL return value", - "type": "Informational", - }, - ], - "success": true, + "error": null, + "issues": [ + { + "address": 626, + "contract": "Unknown", + "debug": "", + "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", + "function": "_function_0x141f32ff", + "max_gas_used": 35856, + "min_gas_used": 1104, + "swc-id": "104", + "title": "Unchecked CALL return value", + "type": "Informational" + }, + { + "address": 857, + "contract": "Unknown", + "debug": "", + "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", + "function": "_function_0x9b58bc26", + "max_gas_used": 35919, + "min_gas_used": 1167, + "swc-id": "104", + "title": "Unchecked CALL return value", + "type": "Informational" + }, + { + "address": 1038, + "contract": "Unknown", + "debug": "", + "description": "This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.", + "function": "_function_0xeea4c864", + "max_gas_used": 1229, + "min_gas_used": 477, + "swc-id": "107", + "title": "Message call to external contract", + "type": "Warning" + }, + { + "address": 1046, + "contract": "Unknown", + "debug": "", + "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", + "function": "_function_0xeea4c864", + "max_gas_used": 35944, + "min_gas_used": 1192, + "swc-id": "104", + "title": "Unchecked CALL return value", + "type": "Informational" + } + ], + "success": true } diff --git a/tests/testdata/outputs_expected/metacoin.sol.o.json b/tests/testdata/outputs_expected/metacoin.sol.o.json new file mode 100644 index 00000000..93a6303f --- /dev/null +++ b/tests/testdata/outputs_expected/metacoin.sol.o.json @@ -0,0 +1 @@ +{"error": null, "issues": [{"address": 498, "contract": "Unknown", "debug": "", "description": "This binary add operation can result in integer overflow.\n", "function": "sendToken(address,uint256)", "max_gas_used": 27415, "min_gas_used": 6750, "swc-id": "101", "title": "Integer Overflow", "type": "Warning"}], "success": true} \ No newline at end of file diff --git a/tests/testdata/outputs_expected/metacoin.sol.o.markdown b/tests/testdata/outputs_expected/metacoin.sol.o.markdown new file mode 100644 index 00000000..a19876e5 --- /dev/null +++ b/tests/testdata/outputs_expected/metacoin.sol.o.markdown @@ -0,0 +1,13 @@ +# Analysis results for test-filename.sol + +## Integer Overflow +- SWC ID: 101 +- Type: Warning +- Contract: Unknown +- Function name: `sendToken(address,uint256)` +- PC address: 498 +- Estimated Gas Usage: 6750 - 27415 + +### Description + +This binary add operation can result in integer overflow. diff --git a/tests/testdata/outputs_expected/metacoin.sol.o.text b/tests/testdata/outputs_expected/metacoin.sol.o.text new file mode 100644 index 00000000..8bcee7d5 --- /dev/null +++ b/tests/testdata/outputs_expected/metacoin.sol.o.text @@ -0,0 +1,11 @@ +==== Integer Overflow ==== +SWC ID: 101 +Type: Warning +Contract: Unknown +Function name: sendToken(address,uint256) +PC address: 498 +Estimated Gas Usage: 6750 - 27415 +This binary add operation can result in integer overflow. + +-------------------- + diff --git a/tests/testdata/outputs_expected/multi_contracts.sol.o.json b/tests/testdata/outputs_expected/multi_contracts.sol.o.json index 891f0203..c4d6e54c 100644 --- a/tests/testdata/outputs_expected/multi_contracts.sol.o.json +++ b/tests/testdata/outputs_expected/multi_contracts.sol.o.json @@ -1,18 +1,18 @@ { - "error": null, - "issues": [ - { - "address": 142, - "contract": "Unknown", - "debug": "", - "description": "Arbitrary senders other than the contract creator can withdraw ETH from the contract account without previously having sent an equivalent amount of ETH to it. This is likely to be a vulnerability.", - "function": "transfer()", - "max_gas_used": 467, - "min_gas_used": 186, - "swc-id": "105", - "title": "Ether thief", - "type": "Warning", - } - ], - "success": true, + "error": null, + "issues": [ + { + "address": 142, + "contract": "Unknown", + "debug": "", + "description": "Arbitrary senders other than the contract creator can withdraw ETH from the contract account without previously having sent an equivalent amount of ETH to it. This is likely to be a vulnerability.", + "function": "transfer()", + "max_gas_used": 467, + "min_gas_used": 186, + "swc-id": "105", + "title": "Ether thief", + "type": "Warning" + } + ], + "success": true } diff --git a/tests/testdata/outputs_expected/nonascii.sol.o.json b/tests/testdata/outputs_expected/nonascii.sol.o.json index 84a01045..179ed787 100644 --- a/tests/testdata/outputs_expected/nonascii.sol.o.json +++ b/tests/testdata/outputs_expected/nonascii.sol.o.json @@ -1 +1,5 @@ -{"error": null, "issues": [], "success": true} +{ + "error": null, + "issues": [], + "success": true +} diff --git a/tests/testdata/outputs_expected/origin.sol.o.json b/tests/testdata/outputs_expected/origin.sol.o.json index 4f756563..49a3d6b1 100644 --- a/tests/testdata/outputs_expected/origin.sol.o.json +++ b/tests/testdata/outputs_expected/origin.sol.o.json @@ -1,18 +1,18 @@ { - "error": null, - "issues": [ - { - "address": 317, - "contract": "Unknown", - "debug": "", - "description": "The function `transferOwnership(address)` retrieves the transaction origin (tx.origin) using the ORIGIN opcode. Use msg.sender instead.\nSee also: https://solidity.readthedocs.io/en/develop/security-considerations.html#tx-origin", - "function": "transferOwnership(address)", - "swc-id": "115", - "min_gas_used": 626, - "max_gas_used": 1051, - "title": "Use of tx.origin", - "type": "Warning", - } - ], - "success": true, + "error": null, + "issues": [ + { + "address": 317, + "contract": "Unknown", + "debug": "", + "description": "The function `transferOwnership(address)` retrieves the transaction origin (tx.origin) using the ORIGIN opcode. Use msg.sender instead.\nSee also: https://solidity.readthedocs.io/en/develop/security-considerations.html#tx-origin", + "function": "transferOwnership(address)", + "max_gas_used": 1051, + "min_gas_used": 626, + "swc-id": "115", + "title": "Use of tx.origin", + "type": "Warning" + } + ], + "success": true } diff --git a/tests/testdata/outputs_expected/overflow.sol.o.json b/tests/testdata/outputs_expected/overflow.sol.o.json index a6a780cb..17b44a75 100644 --- a/tests/testdata/outputs_expected/overflow.sol.o.json +++ b/tests/testdata/outputs_expected/overflow.sol.o.json @@ -1,30 +1,30 @@ { - "error": null, - "issues": [ - { - "address": 567, - "contract": "Unknown", - "debug": "", - "description": "The subtraction can result in an integer underflow.\n", - "function": "sendeth(address,uint256)", - "max_gas_used": 1035, - "min_gas_used": 750, - "swc-id": "101", - "title": "Integer Underflow", - "type": "Warning", - }, - { - "address": 649, - "contract": "Unknown", - "debug": "", - "description": "The subtraction can result in an integer underflow.\n", - "function": "sendeth(address,uint256)", - "max_gas_used": 1758, - "min_gas_used": 1283, - "swc-id": "101", - "title": "Integer Underflow", - "type": "Warning", - }, - ], - "success": true, + "error": null, + "issues": [ + { + "address": 567, + "contract": "Unknown", + "debug": "", + "description": "The subtraction can result in an integer underflow.\n", + "function": "sendeth(address,uint256)", + "max_gas_used": 1035, + "min_gas_used": 750, + "swc-id": "101", + "title": "Integer Underflow", + "type": "Warning" + }, + { + "address": 649, + "contract": "Unknown", + "debug": "", + "description": "The subtraction can result in an integer underflow.\n", + "function": "sendeth(address,uint256)", + "max_gas_used": 1758, + "min_gas_used": 1283, + "swc-id": "101", + "title": "Integer Underflow", + "type": "Warning" + } + ], + "success": true } diff --git a/tests/testdata/outputs_expected/returnvalue.sol.o.json b/tests/testdata/outputs_expected/returnvalue.sol.o.json index f6be6a1e..4d41921e 100644 --- a/tests/testdata/outputs_expected/returnvalue.sol.o.json +++ b/tests/testdata/outputs_expected/returnvalue.sol.o.json @@ -1,18 +1,18 @@ { - "error": null, - "issues": [ - { - "address": 290, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "callnotchecked()", - "max_gas_used": 35941, - "min_gas_used": 1330, - "swc-id": "104", - "title": "Unchecked CALL return value", - "type": "Informational", - } - ], - "success": true, + "error": null, + "issues": [ + { + "address": 290, + "contract": "Unknown", + "debug": "", + "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", + "function": "callnotchecked()", + "max_gas_used": 35941, + "min_gas_used": 1330, + "swc-id": "104", + "title": "Unchecked CALL return value", + "type": "Informational" + } + ], + "success": true } diff --git a/tests/testdata/outputs_expected/suicide.sol.o.json b/tests/testdata/outputs_expected/suicide.sol.o.json index ca43c488..1cb84c82 100644 --- a/tests/testdata/outputs_expected/suicide.sol.o.json +++ b/tests/testdata/outputs_expected/suicide.sol.o.json @@ -1,18 +1,18 @@ { - "error": null, - "issues": [ - { - "address": 146, - "contract": "Unknown", - "debug": "", - "description": "Anyone can kill this contract and withdraw its balance to their own account.", - "function": "kill(address)", - "max_gas_used": 263, - "min_gas_used": 168, - "swc-id": "106", - "title": "Unchecked SUICIDE", - "type": "Warning", - } - ], - "success": true, + "error": null, + "issues": [ + { + "address": 146, + "contract": "Unknown", + "debug": "", + "description": "Anyone can kill this contract and withdraw its balance to their own account.", + "function": "kill(address)", + "max_gas_used": 263, + "min_gas_used": 168, + "swc-id": "106", + "title": "Unchecked SUICIDE", + "type": "Warning" + } + ], + "success": true } diff --git a/tests/testdata/outputs_expected/underflow.sol.o.json b/tests/testdata/outputs_expected/underflow.sol.o.json index a6a780cb..17b44a75 100644 --- a/tests/testdata/outputs_expected/underflow.sol.o.json +++ b/tests/testdata/outputs_expected/underflow.sol.o.json @@ -1,30 +1,30 @@ { - "error": null, - "issues": [ - { - "address": 567, - "contract": "Unknown", - "debug": "", - "description": "The subtraction can result in an integer underflow.\n", - "function": "sendeth(address,uint256)", - "max_gas_used": 1035, - "min_gas_used": 750, - "swc-id": "101", - "title": "Integer Underflow", - "type": "Warning", - }, - { - "address": 649, - "contract": "Unknown", - "debug": "", - "description": "The subtraction can result in an integer underflow.\n", - "function": "sendeth(address,uint256)", - "max_gas_used": 1758, - "min_gas_used": 1283, - "swc-id": "101", - "title": "Integer Underflow", - "type": "Warning", - }, - ], - "success": true, + "error": null, + "issues": [ + { + "address": 567, + "contract": "Unknown", + "debug": "", + "description": "The subtraction can result in an integer underflow.\n", + "function": "sendeth(address,uint256)", + "max_gas_used": 1035, + "min_gas_used": 750, + "swc-id": "101", + "title": "Integer Underflow", + "type": "Warning" + }, + { + "address": 649, + "contract": "Unknown", + "debug": "", + "description": "The subtraction can result in an integer underflow.\n", + "function": "sendeth(address,uint256)", + "max_gas_used": 1758, + "min_gas_used": 1283, + "swc-id": "101", + "title": "Integer Underflow", + "type": "Warning" + } + ], + "success": true } From c9ff1010eabd81488f8f7fb7d100c7e9e6d96391 Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Tue, 27 Nov 2018 00:17:23 +0700 Subject: [PATCH 11/41] Format metacoin.sol.o.json --- .../outputs_expected/metacoin.sol.o.json | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/testdata/outputs_expected/metacoin.sol.o.json b/tests/testdata/outputs_expected/metacoin.sol.o.json index 93a6303f..09f3734f 100644 --- a/tests/testdata/outputs_expected/metacoin.sol.o.json +++ b/tests/testdata/outputs_expected/metacoin.sol.o.json @@ -1 +1,18 @@ -{"error": null, "issues": [{"address": 498, "contract": "Unknown", "debug": "", "description": "This binary add operation can result in integer overflow.\n", "function": "sendToken(address,uint256)", "max_gas_used": 27415, "min_gas_used": 6750, "swc-id": "101", "title": "Integer Overflow", "type": "Warning"}], "success": true} \ No newline at end of file +{ + "error": null, + "issues": [ + { + "address": 498, + "contract": "Unknown", + "debug": "", + "description": "This binary add operation can result in integer overflow.\n", + "function": "sendToken(address,uint256)", + "max_gas_used": 27415, + "min_gas_used": 6750, + "swc-id": "101", + "title": "Integer Overflow", + "type": "Warning" + } + ], + "success": true +} From e2a5308016405cf5b2074f1383200a03322c79a5 Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Tue, 27 Nov 2018 08:02:25 +0700 Subject: [PATCH 12/41] Fix an issue in suicide module --- mythril/analysis/modules/suicide.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mythril/analysis/modules/suicide.py b/mythril/analysis/modules/suicide.py index 7a21b990..c7b13ead 100644 --- a/mythril/analysis/modules/suicide.py +++ b/mythril/analysis/modules/suicide.py @@ -24,11 +24,14 @@ def _analyze_state(state): try: try: transaction_sequence = solver.get_transaction_sequence( - state, node.constraints + state, + node.constraints + [to == 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF], ) description = "Anyone can kill this contract and withdraw its balance to their own account." except UnsatError: - transaction_sequence = solver.get_transaction_sequence(state, constraints) + transaction_sequence = solver.get_transaction_sequence( + state, node.constraints + ) description = ( "The contract can be killed by anyone. Don't accidentally kill it." ) From 960b7f1f5a12800312b6da6af3f00ebca0de8e4d Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Tue, 27 Nov 2018 22:57:42 +0700 Subject: [PATCH 13/41] Update tests --- tests/testdata/outputs_expected/calls.sol.o.json | 2 +- tests/testdata/outputs_expected/calls.sol.o.markdown | 2 +- tests/testdata/outputs_expected/calls.sol.o.text | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/testdata/outputs_expected/calls.sol.o.json b/tests/testdata/outputs_expected/calls.sol.o.json index 6d4d8063..98731f5f 100644 --- a/tests/testdata/outputs_expected/calls.sol.o.json +++ b/tests/testdata/outputs_expected/calls.sol.o.json @@ -30,7 +30,7 @@ "contract": "Unknown", "debug": "", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "_function_0xe11f493e", + "function": "reentrancy()", "max_gas_used": 61043, "min_gas_used": 6432, "swc-id": "104", diff --git a/tests/testdata/outputs_expected/calls.sol.o.markdown b/tests/testdata/outputs_expected/calls.sol.o.markdown index 297047d2..8e2b94e0 100644 --- a/tests/testdata/outputs_expected/calls.sol.o.markdown +++ b/tests/testdata/outputs_expected/calls.sol.o.markdown @@ -28,7 +28,7 @@ The return value of an external call is not checked. Note that execution continu - SWC ID: 104 - Type: Informational - Contract: Unknown -- Function name: `_function_0xe11f493e` +- Function name: `reentrancy()` - PC address: 871 - Estimated Gas Usage: 6432 - 61043 diff --git a/tests/testdata/outputs_expected/calls.sol.o.text b/tests/testdata/outputs_expected/calls.sol.o.text index 02407bee..38bf9e55 100644 --- a/tests/testdata/outputs_expected/calls.sol.o.text +++ b/tests/testdata/outputs_expected/calls.sol.o.text @@ -22,7 +22,7 @@ The return value of an external call is not checked. Note that execution continu SWC ID: 104 Type: Informational Contract: Unknown -Function name: _function_0xe11f493e +Function name: reentrancy() PC address: 871 Estimated Gas Usage: 6432 - 61043 The return value of an external call is not checked. Note that execution continue even if the called contract throws. From 4b9421863a3427df2cc53c753683657a2363fcc4 Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Tue, 27 Nov 2018 23:03:59 +0700 Subject: [PATCH 14/41] Attempt to fix tests again --- tests/testdata/outputs_expected/calls.sol.o.json | 2 +- tests/testdata/outputs_expected/calls.sol.o.markdown | 2 +- tests/testdata/outputs_expected/calls.sol.o.text | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/testdata/outputs_expected/calls.sol.o.json b/tests/testdata/outputs_expected/calls.sol.o.json index 98731f5f..6d4d8063 100644 --- a/tests/testdata/outputs_expected/calls.sol.o.json +++ b/tests/testdata/outputs_expected/calls.sol.o.json @@ -30,7 +30,7 @@ "contract": "Unknown", "debug": "", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "reentrancy()", + "function": "_function_0xe11f493e", "max_gas_used": 61043, "min_gas_used": 6432, "swc-id": "104", diff --git a/tests/testdata/outputs_expected/calls.sol.o.markdown b/tests/testdata/outputs_expected/calls.sol.o.markdown index 8e2b94e0..297047d2 100644 --- a/tests/testdata/outputs_expected/calls.sol.o.markdown +++ b/tests/testdata/outputs_expected/calls.sol.o.markdown @@ -28,7 +28,7 @@ The return value of an external call is not checked. Note that execution continu - SWC ID: 104 - Type: Informational - Contract: Unknown -- Function name: `reentrancy()` +- Function name: `_function_0xe11f493e` - PC address: 871 - Estimated Gas Usage: 6432 - 61043 diff --git a/tests/testdata/outputs_expected/calls.sol.o.text b/tests/testdata/outputs_expected/calls.sol.o.text index 38bf9e55..02407bee 100644 --- a/tests/testdata/outputs_expected/calls.sol.o.text +++ b/tests/testdata/outputs_expected/calls.sol.o.text @@ -22,7 +22,7 @@ The return value of an external call is not checked. Note that execution continu SWC ID: 104 Type: Informational Contract: Unknown -Function name: reentrancy() +Function name: _function_0xe11f493e PC address: 871 Estimated Gas Usage: 6432 - 61043 The return value of an external call is not checked. Note that execution continue even if the called contract throws. From 9a5479266c4c23f1388a7cd07b21a2b73720bad5 Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Wed, 28 Nov 2018 10:07:09 +0700 Subject: [PATCH 15/41] Re-add and fix metacoin sample --- tests/testdata/input_contracts/calls.sol | 5 +++-- tests/testdata/inputs/metacoin.sol.o | 1 + tests/testdata/outputs_expected/metacoin.sol.o.json | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 tests/testdata/inputs/metacoin.sol.o diff --git a/tests/testdata/input_contracts/calls.sol b/tests/testdata/input_contracts/calls.sol index 513e31fb..05b388f0 100644 --- a/tests/testdata/input_contracts/calls.sol +++ b/tests/testdata/input_contracts/calls.sol @@ -9,7 +9,7 @@ contract Caller { uint256 statevar; constructor(address addr) public { - fixed_address = addr; + fixed_address = "0x552254cbaaf32613c6c0450cf19524594ef84044"; } function thisisfine() public { @@ -27,10 +27,11 @@ contract Caller { function callstoredaddress() public { stored_address.call(""); + statevar = 0; } function setstoredaddress(address addr) public { stored_address = addr; } -} \ No newline at end of file +} diff --git a/tests/testdata/inputs/metacoin.sol.o b/tests/testdata/inputs/metacoin.sol.o new file mode 100644 index 00000000..87e952f3 --- /dev/null +++ b/tests/testdata/inputs/metacoin.sol.o @@ -0,0 +1 @@ +60606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806327e235e314610051578063412664ae1461009e575b600080fd5b341561005c57600080fd5b610088600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506100f8565b6040518082815260200191505060405180910390f35b34156100a957600080fd5b6100de600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610110565b604051808215151515815260200191505060405180910390f35b60006020528060005260406000206000915090505481565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101561016157600090506101fe565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550600090505b929150505600a165627a7a72305820c860d60246e215343f02c5025aeef4ad1f207b0a7d2dec05e43f6ecaaebe9cec0029 \ No newline at end of file diff --git a/tests/testdata/outputs_expected/metacoin.sol.o.json b/tests/testdata/outputs_expected/metacoin.sol.o.json index 09f3734f..4c976ff9 100644 --- a/tests/testdata/outputs_expected/metacoin.sol.o.json +++ b/tests/testdata/outputs_expected/metacoin.sol.o.json @@ -1,6 +1,6 @@ { "error": null, - "issues": [ + "issues": [] { "address": 498, "contract": "Unknown", From f9519367a673f70de21f483d32d057520fad9c70 Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Wed, 28 Nov 2018 10:20:08 +0700 Subject: [PATCH 16/41] Update calls.sol for solc 0.5.0 --- tests/testdata/input_contracts/calls.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testdata/input_contracts/calls.sol b/tests/testdata/input_contracts/calls.sol index 05b388f0..cc7998ee 100644 --- a/tests/testdata/input_contracts/calls.sol +++ b/tests/testdata/input_contracts/calls.sol @@ -9,7 +9,7 @@ contract Caller { uint256 statevar; constructor(address addr) public { - fixed_address = "0x552254cbaaf32613c6c0450cf19524594ef84044"; + fixed_address = address(0x552254CbAaF32613C6c0450CF19524594eF84044); } function thisisfine() public { From 66149578bc8767d35f6228b2cf5f5102a0f9eafa Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Wed, 28 Nov 2018 11:14:51 +0700 Subject: [PATCH 17/41] Refactor external calls module --- mythril/analysis/modules/delegatecall.py | 43 ---- mythril/analysis/modules/external_calls.py | 259 ++++++--------------- 2 files changed, 70 insertions(+), 232 deletions(-) diff --git a/mythril/analysis/modules/delegatecall.py b/mythril/analysis/modules/delegatecall.py index 5098b9c2..7d70e735 100644 --- a/mythril/analysis/modules/delegatecall.py +++ b/mythril/analysis/modules/delegatecall.py @@ -37,9 +37,6 @@ class DelegateCallModule(DetectionModule): if meminstart.type == VarType.CONCRETE: issues += self._concrete_call(call, state, address, meminstart) - if call.to.type == VarType.SYMBOLIC: - issues += self._symbolic_call(call, state, address, statespace) - return issues def _concrete_call(self, call, state, address, meminstart): @@ -68,45 +65,5 @@ class DelegateCallModule(DetectionModule): return [issue] - def _symbolic_call(self, call, state, address, statespace): - issue = Issue( - contract=call.node.contract_name, - function_name=call.node.function_name, - address=address, - swc_id=DELEGATECALL_TO_UNTRUSTED_CONTRACT, - bytecode=state.environment.code.bytecode, - title=call.type + " to a user-supplied address", - gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used), - ) - - if "calldata" in str(call.to): - issue.description = "This contract delegates execution to a contract address obtained from calldata." - - else: - m = re.search(r"storage_([a-z0-9_&^]+)", str(call.to)) - - if m: - idx = m.group(1) - func = statespace.find_storage_write( - state.environment.active_account.address, idx - ) - - if func: - issue.description = ( - "This contract delegates execution to a contract address in storage slot " - + str(idx) - + ". This storage slot can be written to by calling the function `" - + func - + "`." - ) - - else: - logging.debug( - "[DELEGATECALL] No storage writes to index " + str(idx) - ) - - issue.description += " Be aware that the called contract gets unrestricted access to this contract's state." - return [issue] - detector = DelegateCallModule() diff --git a/mythril/analysis/modules/external_calls.py b/mythril/analysis/modules/external_calls.py index 3a2a089b..4e073626 100644 --- a/mythril/analysis/modules/external_calls.py +++ b/mythril/analysis/modules/external_calls.py @@ -4,9 +4,8 @@ from mythril.analysis.report import Issue from mythril.analysis import solver from mythril.analysis.swc_data import REENTRANCY from mythril.analysis.modules.base import DetectionModule -import re +from mythril.exceptions import UnsatError import logging -from mythril.laser.ethereum.cfg import JumpType class ExternalCallModule(DetectionModule): @@ -20,199 +19,81 @@ class ExternalCallModule(DetectionModule): self.max_search_depth = max_search_depth self.calls_visited = [] - def search_children( - self, statespace, node, transaction_id, start_index=0, depth=0, results=None - ): - if results is None: - results = [] - logging.debug("SEARCHING NODE %d", node.uid) - - if depth < self.max_search_depth: - - n_states = len(node.states) - - if n_states > start_index: - - for j in range(start_index, n_states): - if ( - node.states[j].get_current_instruction()["opcode"] == "SSTORE" - and node.states[j].current_transaction.id == transaction_id - ): - results.append( - node.states[j].get_current_instruction()["address"] - ) - children = [] - - for edge in statespace.edges: - if edge.node_from == node.uid and edge.type != JumpType.Transaction: - children.append(statespace.nodes[edge.node_to]) - - if len(children): - for node in children: - results += self.search_children( - statespace, - node, - transaction_id, - depth=depth + 1, - results=results, - ) - - return results - - def execute(self, statespace): - + def execute(self, state_space): + logging.debug("Executing module: %s", self.name) issues = [] - for call in statespace.calls: + for k in state_space.nodes: + node = state_space.nodes[k] + for state in node.states: + issues += self._analyze_state(state, node) - state = call.state - address = state.get_current_instruction()["address"] - - if call.type == "CALL": + return issues - logging.debug( - "[EXTERNAL_CALLS] Call to: %s, value = %s, gas = %s" - % (str(call.to), str(call.value), str(call.gas)) + @staticmethod + def _analyze_state(state, node): + issues = [] + instruction = state.get_current_instruction() + + if instruction["opcode"] not in ("CALL", "CALLCODE", "DELEGATECALL", "STATICCALL"): + return [] + + gas = state.mstate.stack[-1] + to = state.mstate.stack[-2] + + address = state.get_current_instruction()["address"] + + try: + constraints = state.node.constraints + [gas > 2300] + transaction_sequence = solver.get_transaction_sequence(state, constraints) + + try: + constraints += [to == 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF] + transaction_sequence = solver.get_transaction_sequence(state, constraints) + + except UnsatError: + debug = str(transaction_sequence) + description = "The contract executes a function call to an external address. " \ + "Verify that the code at this address is trusted and immutable." + + issue = Issue( + contract=node.contract_name, + function_name=state.node.function_name, + address=address, + swc_id=REENTRANCY, + title="External call", + _type="Informational", + bytecode=state.environment.code.bytecode, + description=description, + debug=debug, + gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used), ) - if ( - call.to.type == VarType.SYMBOLIC - and (call.gas.type == VarType.CONCRETE and call.gas.val > 2300) - or ( - call.gas.type == VarType.SYMBOLIC - and "2300" not in str(call.gas) - ) - ): - - description = "This contract executes a message call to " - - target = str(call.to) - user_supplied = False - - if "calldata" in target or "caller" in target: - - if "calldata" in target: - description += ( - "an address provided as a function argument. " - ) - else: - description += "the address of the transaction sender. " - - user_supplied = True - else: - m = re.search(r"storage_([a-z0-9_&^]+)", str(call.to)) - - if m: - idx = m.group(1) - - func = statespace.find_storage_write( - state.environment.active_account.address, idx - ) - - if func: - - description += ( - "an address found at storage slot " - + str(idx) - + ". " - + "This storage slot can be written to by calling the function `" - + func - + "`. " - ) - user_supplied = True - - if user_supplied: - - description += ( - "Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. " - "Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state." - ) - - issue = Issue( - contract=call.node.contract_name, - function_name=call.node.function_name, - address=address, - title="Message call to external contract", - _type="Warning", - description=description, - bytecode=state.environment.code.bytecode, - swc_id=REENTRANCY, - gas_used=( - state.mstate.min_gas_used, - state.mstate.max_gas_used, - ), - ) - - else: - - description += "to another contract. Make sure that the called contract is trusted and does not execute user-supplied code." - - issue = Issue( - contract=call.node.contract_name, - function_name=call.node.function_name, - address=address, - title="Message call to external contract", - _type="Informational", - description=description, - bytecode=state.environment.code.bytecode, - swc_id=REENTRANCY, - gas_used=( - state.mstate.min_gas_used, - state.mstate.max_gas_used, - ), - ) - - issues.append(issue) - - if address not in self.calls_visited: - self.calls_visited.append(address) - - logging.debug( - "[EXTERNAL_CALLS] Checking for state changes starting from " - + call.node.function_name - ) - - # Check for SSTORE in remaining instructions in current node & nodes down the CFG - - state_change_addresses = self.search_children( - statespace, - call.node, - call.state.current_transaction.id, - call.state_index + 1, - depth=0, - results=[], - ) - - logging.debug( - "[EXTERNAL_CALLS] Detected state changes at addresses: " - + str(state_change_addresses) - ) - - if len(state_change_addresses): - for address in state_change_addresses: - description = ( - "The contract account state is changed after an external call. " - "Consider that the called contract could re-enter the function before this " - "state change takes place. This can lead to business logic vulnerabilities." - ) - - issue = Issue( - contract=call.node.contract_name, - function_name=call.node.function_name, - address=address, - title="State change after external call", - _type="Warning", - description=description, - bytecode=state.environment.code.bytecode, - swc_id=REENTRANCY, - gas_used=( - state.mstate.min_gas_used, - state.mstate.max_gas_used, - ), - ) - issues.append(issue) - - return issues + issues.append(issue) + return issues + + debug = str(transaction_sequence) + description = "The contract executes a function call with high gas to a user-supplied address. " \ + "Note that the callee can contain arbitrary code and may re-enter any function in this contract. " \ + "Review the business logic carefully to prevent unanticipated effects on the contract state." + + issue = Issue( + contract=node.contract_name, + function_name=node.function_name, + address=address, + swc_id=REENTRANCY, + title="External call to user-supplied address", + _type="Warning", + bytecode=state.environment.code.bytecode, + description=description, + debug=debug, + gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used), + ) + issues.append(issue) + return issues + + except UnsatError: + logging.debug("[EXTERNAL_CALLS] no model found for setting caller address.") detector = ExternalCallModule() From a0b17219896cdbe3fa2663f2bfe2dbf8288b3001 Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Wed, 28 Nov 2018 11:34:02 +0700 Subject: [PATCH 18/41] Remove unused vars --- mythril/analysis/modules/external_calls.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/mythril/analysis/modules/external_calls.py b/mythril/analysis/modules/external_calls.py index 4e073626..0562ca9c 100644 --- a/mythril/analysis/modules/external_calls.py +++ b/mythril/analysis/modules/external_calls.py @@ -8,16 +8,23 @@ from mythril.exceptions import UnsatError import logging +DESCRIPTION = """ + +Search for low level calls (e.g. call.value()) that forward all gas to the callee. +Report a warning if the callee address can be set by the sender, otherwise create +an informational issue. + +""" + + class ExternalCallModule(DetectionModule): - def __init__(self, max_search_depth=64): + def __init__(self): super().__init__( name="External Calls", swc_id=REENTRANCY, - hooks=["CALL"], + hooks=["CALL", "DELEGATECALL", "STATICCALL", "CALLCODE"], description="Check for call.value()() to external addresses", ) - self.max_search_depth = max_search_depth - self.calls_visited = [] def execute(self, state_space): logging.debug("Executing module: %s", self.name) From e441907c38c06d20f45b4ba28a8bcd6840556242 Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Wed, 28 Nov 2018 12:31:37 +0700 Subject: [PATCH 19/41] Rewrite external_calls, refactor thief & extcalls to callbacks --- mythril/analysis/modules/ether_thief.py | 114 +++++++++---------- mythril/analysis/modules/external_calls.py | 123 ++++++++++----------- mythril/laser/ethereum/instructions.py | 2 +- 3 files changed, 118 insertions(+), 121 deletions(-) diff --git a/mythril/analysis/modules/ether_thief.py b/mythril/analysis/modules/ether_thief.py index 315c9e64..101b1b8b 100644 --- a/mythril/analysis/modules/ether_thief.py +++ b/mythril/analysis/modules/ether_thief.py @@ -3,6 +3,7 @@ from mythril.analysis import solver from mythril.analysis.report import Issue from mythril.analysis.swc_data import UNPROTECTED_ETHER_WITHDRAWAL from mythril.analysis.modules.base import DetectionModule +from mythril.laser.ethereum.state.global_state import GlobalState from mythril.exceptions import UnsatError from z3 import BitVecVal, UGT, Sum import logging @@ -22,80 +23,79 @@ An issue is reported if: """ -class EtherThief(DetectionModule): - def __init__(self): - super().__init__( - name="Ether Thief", - swc_id=UNPROTECTED_ETHER_WITHDRAWAL, - hooks=["CALL"], - description=DESCRIPTION, - ) +def _analyze_state(state): + instruction = state.get_current_instruction() + node = state.node - def execute(self, state_space): - logging.debug("Executing module: %s", self.name) - issues = [] + if instruction["opcode"] != "CALL": + return [] - for k in state_space.nodes: - node = state_space.nodes[k] - for state in node.states: - issues += self._analyze_state(state, node) + call_value = state.mstate.stack[-3] + target = state.mstate.stack[-2] - return issues + eth_sent_total = BitVecVal(0, 256) - @staticmethod - def _analyze_state(state, node): - issues = [] - instruction = state.get_current_instruction() + constraints = node.constraints - if instruction["opcode"] != "CALL": - return [] + for tx in state.world_state.transaction_sequence: + if tx.caller == 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF: - call_value = state.mstate.stack[-3] - target = state.mstate.stack[-2] + # There's sometimes no overflow check on balances added. + # But we don't care about attacks that require more 2^^256 ETH to be sent. - eth_sent_total = BitVecVal(0, 256) + constraints += [BVAddNoOverflow(eth_sent_total, tx.call_value, False)] + eth_sent_total = Sum(eth_sent_total, tx.call_value) - constraints = node.constraints + constraints += [ + UGT(call_value, eth_sent_total), + target == state.environment.sender, + ] - for tx in state.world_state.transaction_sequence: - if tx.caller == 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF: + try: - # There's sometimes no overflow check on balances added. - # But we don't care about attacks that require more 2^^256 ETH to be sent. + transaction_sequence = solver.get_transaction_sequence(state, constraints) - constraints += [BVAddNoOverflow(eth_sent_total, tx.call_value, False)] - eth_sent_total = Sum(eth_sent_total, tx.call_value) + debug = str(transaction_sequence) - constraints += [ - UGT(call_value, eth_sent_total), - target == state.environment.sender, - ] + issue = Issue( + contract=node.contract_name, + function_name=node.function_name, + address=instruction["address"], + swc_id=UNPROTECTED_ETHER_WITHDRAWAL, + title="Ether thief", + _type="Warning", + bytecode=state.environment.code.bytecode, + description="Arbitrary senders other than the contract creator can withdraw ETH from the contract" + + " account without previously having sent an equivalent amount of ETH to it. This is likely to be" + + " a vulnerability.", + debug=debug, + gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used), + ) + except UnsatError: + logging.debug("[ETHER_THIEF] no model found") + return [] - try: + return [issue] - transaction_sequence = solver.get_transaction_sequence(state, constraints) - debug = str(transaction_sequence) +class EtherThief(DetectionModule): + def __init__(self): + super().__init__( + name="Ether Thief", + swc_id=UNPROTECTED_ETHER_WITHDRAWAL, + hooks=["CALL"], + description=DESCRIPTION, + entrypoint="callback", + ) + self._issues = [] - issue = Issue( - contract=node.contract_name, - function_name=node.function_name, - address=instruction["address"], - swc_id=UNPROTECTED_ETHER_WITHDRAWAL, - title="Ether thief", - _type="Warning", - bytecode=state.environment.code.bytecode, - description="Arbitrary senders other than the contract creator can withdraw ETH from the contract" - + " account without previously having sent an equivalent amount of ETH to it. This is likely to be" - + " a vulnerability.", - debug=debug, - gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used), - ) - issues.append(issue) - except UnsatError: - logging.debug("[ETHER_THIEF] no model found") + def execute(self, state: GlobalState): + self._issues.extend(_analyze_state(state)) + return self.issues - return issues + @property + def issues(self): + return self._issues detector = EtherThief() diff --git a/mythril/analysis/modules/external_calls.py b/mythril/analysis/modules/external_calls.py index 0562ca9c..2476d5e0 100644 --- a/mythril/analysis/modules/external_calls.py +++ b/mythril/analysis/modules/external_calls.py @@ -1,88 +1,43 @@ from z3 import * -from mythril.analysis.ops import * from mythril.analysis.report import Issue from mythril.analysis import solver from mythril.analysis.swc_data import REENTRANCY from mythril.analysis.modules.base import DetectionModule +from mythril.laser.ethereum.state.global_state import GlobalState from mythril.exceptions import UnsatError import logging - DESCRIPTION = """ -Search for low level calls (e.g. call.value()) that forward all gas to the callee. +Search for low level calls (e.g. call.value()) that forward all gas to the callee. Report a warning if the callee address can be set by the sender, otherwise create an informational issue. """ -class ExternalCallModule(DetectionModule): - def __init__(self): - super().__init__( - name="External Calls", - swc_id=REENTRANCY, - hooks=["CALL", "DELEGATECALL", "STATICCALL", "CALLCODE"], - description="Check for call.value()() to external addresses", - ) - - def execute(self, state_space): - logging.debug("Executing module: %s", self.name) - issues = [] - - for k in state_space.nodes: - node = state_space.nodes[k] - for state in node.states: - issues += self._analyze_state(state, node) - - return issues +def _analyze_state(state): - @staticmethod - def _analyze_state(state, node): - issues = [] - instruction = state.get_current_instruction() + node = state.node + gas = state.mstate.stack[-1] + to = state.mstate.stack[-2] - if instruction["opcode"] not in ("CALL", "CALLCODE", "DELEGATECALL", "STATICCALL"): - return [] + address = state.get_current_instruction()["address"] - gas = state.mstate.stack[-1] - to = state.mstate.stack[-2] + try: + constraints = node.constraints + [gas > 2300] + transaction_sequence = solver.get_transaction_sequence(state, constraints) - address = state.get_current_instruction()["address"] + # Check whether we can also set the callee address try: - constraints = state.node.constraints + [gas > 2300] + constraints += [to == 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF] transaction_sequence = solver.get_transaction_sequence(state, constraints) - try: - constraints += [to == 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF] - transaction_sequence = solver.get_transaction_sequence(state, constraints) - - except UnsatError: - debug = str(transaction_sequence) - description = "The contract executes a function call to an external address. " \ - "Verify that the code at this address is trusted and immutable." - - issue = Issue( - contract=node.contract_name, - function_name=state.node.function_name, - address=address, - swc_id=REENTRANCY, - title="External call", - _type="Informational", - bytecode=state.environment.code.bytecode, - description=description, - debug=debug, - gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used), - ) - - issues.append(issue) - return issues - debug = str(transaction_sequence) description = "The contract executes a function call with high gas to a user-supplied address. " \ - "Note that the callee can contain arbitrary code and may re-enter any function in this contract. " \ - "Review the business logic carefully to prevent unanticipated effects on the contract state." + "Note that the callee can contain arbitrary code and may re-enter any function in this contract. " \ + "Review the business logic carefully to prevent unanticipated effects on the contract state." issue = Issue( contract=node.contract_name, @@ -96,11 +51,53 @@ class ExternalCallModule(DetectionModule): debug=debug, gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used), ) - issues.append(issue) - return issues except UnsatError: - logging.debug("[EXTERNAL_CALLS] no model found for setting caller address.") + + logging.debug("[EXTERNAL_CALLS] Callee address cannot be modified. Reporting informational issue.") + + debug = str(transaction_sequence) + description = "The contract executes a function call to an external address. " \ + "Verify that the code at this address is trusted and immutable." + + issue = Issue( + contract=node.contract_name, + function_name=state.node.function_name, + address=address, + swc_id=REENTRANCY, + title="External call", + _type="Informational", + bytecode=state.environment.code.bytecode, + description=description, + debug=debug, + gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used), + ) + + except UnsatError: + logging.debug("[EXTERNAL_CALLS] No model found.") + return [] + + return [issue] + + +class ExternalCalls(DetectionModule): + def __init__(self): + super().__init__( + name="External calls", + swc_id=REENTRANCY, + hooks=["CALL"], + description=(DESCRIPTION), + entrypoint="callback", + ) + self._issues = [] + + def execute(self, state: GlobalState): + self._issues.extend(_analyze_state(state)) + return self.issues + + @property + def issues(self): + return self._issues -detector = ExternalCallModule() +detector = ExternalCalls() diff --git a/mythril/laser/ethereum/instructions.py b/mythril/laser/ethereum/instructions.py index 7ade087d..16f2fc82 100644 --- a/mythril/laser/ethereum/instructions.py +++ b/mythril/laser/ethereum/instructions.py @@ -131,7 +131,7 @@ class Instruction: def evaluate(self, global_state: GlobalState, post=False) -> List[GlobalState]: """ Performs the mutation for this instruction """ # Generalize some ops - logging.debug("Evaluating {}".format(self.op_code)) + # logging.debug("Evaluating {}".format(self.op_code)) op = self.op_code.lower() if self.op_code.startswith("PUSH"): op = "push" From 39075160e7570f7ced796c1a252a93e8526c6892 Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Wed, 28 Nov 2018 12:36:47 +0700 Subject: [PATCH 20/41] Black formatting --- mythril/analysis/modules/ether_thief.py | 5 +---- mythril/analysis/modules/external_calls.py | 20 +++++++++++++------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/mythril/analysis/modules/ether_thief.py b/mythril/analysis/modules/ether_thief.py index 101b1b8b..387377c1 100644 --- a/mythril/analysis/modules/ether_thief.py +++ b/mythril/analysis/modules/ether_thief.py @@ -46,10 +46,7 @@ def _analyze_state(state): constraints += [BVAddNoOverflow(eth_sent_total, tx.call_value, False)] eth_sent_total = Sum(eth_sent_total, tx.call_value) - constraints += [ - UGT(call_value, eth_sent_total), - target == state.environment.sender, - ] + constraints += [UGT(call_value, eth_sent_total), target == state.environment.sender] try: diff --git a/mythril/analysis/modules/external_calls.py b/mythril/analysis/modules/external_calls.py index 2476d5e0..e3cf7ce9 100644 --- a/mythril/analysis/modules/external_calls.py +++ b/mythril/analysis/modules/external_calls.py @@ -25,7 +25,7 @@ def _analyze_state(state): address = state.get_current_instruction()["address"] try: - constraints = node.constraints + [gas > 2300] + constraints = node.constraints transaction_sequence = solver.get_transaction_sequence(state, constraints) # Check whether we can also set the callee address @@ -35,9 +35,11 @@ def _analyze_state(state): transaction_sequence = solver.get_transaction_sequence(state, constraints) debug = str(transaction_sequence) - description = "The contract executes a function call with high gas to a user-supplied address. " \ - "Note that the callee can contain arbitrary code and may re-enter any function in this contract. " \ - "Review the business logic carefully to prevent unanticipated effects on the contract state." + description = ( + "The contract executes a function call with high gas to a user-supplied address. " + "Note that the callee can contain arbitrary code and may re-enter any function in this contract. " + "Review the business logic carefully to prevent unanticipated effects on the contract state." + ) issue = Issue( contract=node.contract_name, @@ -54,11 +56,15 @@ def _analyze_state(state): except UnsatError: - logging.debug("[EXTERNAL_CALLS] Callee address cannot be modified. Reporting informational issue.") + logging.debug( + "[EXTERNAL_CALLS] Callee address cannot be modified. Reporting informational issue." + ) debug = str(transaction_sequence) - description = "The contract executes a function call to an external address. " \ - "Verify that the code at this address is trusted and immutable." + description = ( + "The contract executes a function call to an external address. " + "Verify that the code at this address is trusted and immutable." + ) issue = Issue( contract=node.contract_name, From 008e607d4706a75ec85358ca2bba88a9aff8d9dd Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Wed, 28 Nov 2018 13:01:08 +0700 Subject: [PATCH 21/41] Formatting --- mythril/analysis/modules/external_calls.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mythril/analysis/modules/external_calls.py b/mythril/analysis/modules/external_calls.py index e3cf7ce9..93ead404 100644 --- a/mythril/analysis/modules/external_calls.py +++ b/mythril/analysis/modules/external_calls.py @@ -26,7 +26,9 @@ def _analyze_state(state): try: constraints = node.constraints - transaction_sequence = solver.get_transaction_sequence(state, constraints) + transaction_sequence = solver.get_transaction_sequence( + state, constraints + [UGT(gas, 2300)] + ) # Check whether we can also set the callee address From 67b9058a0ae65f66ed03162f3f167772d1081305 Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Wed, 28 Nov 2018 13:10:21 +0700 Subject: [PATCH 22/41] Remove extra arg in cli.py that breaks callgraph generation --- mythril/interfaces/cli.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mythril/interfaces/cli.py b/mythril/interfaces/cli.py index 90a5975d..8e4867de 100644 --- a/mythril/interfaces/cli.py +++ b/mythril/interfaces/cli.py @@ -385,7 +385,6 @@ def main(): max_depth=args.max_depth, execution_timeout=args.execution_timeout, create_timeout=args.create_timeout, - transaction_count=1, ) try: From d72afe308956007703bb3efa8d5b782a15dadca0 Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Wed, 28 Nov 2018 13:33:17 +0700 Subject: [PATCH 23/41] Add missing files --- .../outputs_expected/metacoin.sol.o.easm | 253 ++++++++++++++++++ .../metacoin.sol.o.graph.html | 62 +++++ .../outputs_expected/metacoin.sol.o.json | 15 +- .../outputs_expected/metacoin.sol.o.markdown | 14 +- .../outputs_expected/metacoin.sol.o.text | 12 +- 5 files changed, 319 insertions(+), 37 deletions(-) create mode 100644 tests/testdata/outputs_expected/metacoin.sol.o.easm create mode 100644 tests/testdata/outputs_expected/metacoin.sol.o.graph.html diff --git a/tests/testdata/outputs_expected/metacoin.sol.o.easm b/tests/testdata/outputs_expected/metacoin.sol.o.easm new file mode 100644 index 00000000..fbb53181 --- /dev/null +++ b/tests/testdata/outputs_expected/metacoin.sol.o.easm @@ -0,0 +1,253 @@ +0 PUSH1 0x60 +2 PUSH1 0x40 +4 MSTORE +5 PUSH1 0x04 +7 CALLDATASIZE +8 LT +9 PUSH2 0x004c +12 JUMPI +13 PUSH1 0x00 +15 CALLDATALOAD +16 PUSH29 0x0100000000000000000000000000000000000000000000000000000000 +46 SWAP1 +47 DIV +48 PUSH4 0xffffffff +53 AND +54 DUP1 +55 PUSH4 0x27e235e3 +60 EQ +61 PUSH2 0x0051 +64 JUMPI +65 DUP1 +66 PUSH4 0x412664ae +71 EQ +72 PUSH2 0x009e +75 JUMPI +76 JUMPDEST +77 PUSH1 0x00 +79 DUP1 +80 REVERT +81 JUMPDEST +82 CALLVALUE +83 ISZERO +84 PUSH2 0x005c +87 JUMPI +88 PUSH1 0x00 +90 DUP1 +91 REVERT +92 JUMPDEST +93 PUSH2 0x0088 +96 PUSH1 0x04 +98 DUP1 +99 DUP1 +100 CALLDATALOAD +101 PUSH20 0xffffffffffffffffffffffffffffffffffffffff +122 AND +123 SWAP1 +124 PUSH1 0x20 +126 ADD +127 SWAP1 +128 SWAP2 +129 SWAP1 +130 POP +131 POP +132 PUSH2 0x00f8 +135 JUMP +136 JUMPDEST +137 PUSH1 0x40 +139 MLOAD +140 DUP1 +141 DUP3 +142 DUP2 +143 MSTORE +144 PUSH1 0x20 +146 ADD +147 SWAP2 +148 POP +149 POP +150 PUSH1 0x40 +152 MLOAD +153 DUP1 +154 SWAP2 +155 SUB +156 SWAP1 +157 RETURN +158 JUMPDEST +159 CALLVALUE +160 ISZERO +161 PUSH2 0x00a9 +164 JUMPI +165 PUSH1 0x00 +167 DUP1 +168 REVERT +169 JUMPDEST +170 PUSH2 0x00de +173 PUSH1 0x04 +175 DUP1 +176 DUP1 +177 CALLDATALOAD +178 PUSH20 0xffffffffffffffffffffffffffffffffffffffff +199 AND +200 SWAP1 +201 PUSH1 0x20 +203 ADD +204 SWAP1 +205 SWAP2 +206 SWAP1 +207 DUP1 +208 CALLDATALOAD +209 SWAP1 +210 PUSH1 0x20 +212 ADD +213 SWAP1 +214 SWAP2 +215 SWAP1 +216 POP +217 POP +218 PUSH2 0x0110 +221 JUMP +222 JUMPDEST +223 PUSH1 0x40 +225 MLOAD +226 DUP1 +227 DUP3 +228 ISZERO +229 ISZERO +230 ISZERO +231 ISZERO +232 DUP2 +233 MSTORE +234 PUSH1 0x20 +236 ADD +237 SWAP2 +238 POP +239 POP +240 PUSH1 0x40 +242 MLOAD +243 DUP1 +244 SWAP2 +245 SUB +246 SWAP1 +247 RETURN +248 JUMPDEST +249 PUSH1 0x00 +251 PUSH1 0x20 +253 MSTORE +254 DUP1 +255 PUSH1 0x00 +257 MSTORE +258 PUSH1 0x40 +260 PUSH1 0x00 +262 SHA3 +263 PUSH1 0x00 +265 SWAP2 +266 POP +267 SWAP1 +268 POP +269 SLOAD +270 DUP2 +271 JUMP +272 JUMPDEST +273 PUSH1 0x00 +275 DUP2 +276 PUSH1 0x00 +278 DUP1 +279 CALLER +280 PUSH20 0xffffffffffffffffffffffffffffffffffffffff +301 AND +302 PUSH20 0xffffffffffffffffffffffffffffffffffffffff +323 AND +324 DUP2 +325 MSTORE +326 PUSH1 0x20 +328 ADD +329 SWAP1 +330 DUP2 +331 MSTORE +332 PUSH1 0x20 +334 ADD +335 PUSH1 0x00 +337 SHA3 +338 SLOAD +339 LT +340 ISZERO +341 PUSH2 0x0161 +344 JUMPI +345 PUSH1 0x00 +347 SWAP1 +348 POP +349 PUSH2 0x01fe +352 JUMP +353 JUMPDEST +354 DUP2 +355 PUSH1 0x00 +357 DUP1 +358 CALLER +359 PUSH20 0xffffffffffffffffffffffffffffffffffffffff +380 AND +381 PUSH20 0xffffffffffffffffffffffffffffffffffffffff +402 AND +403 DUP2 +404 MSTORE +405 PUSH1 0x20 +407 ADD +408 SWAP1 +409 DUP2 +410 MSTORE +411 PUSH1 0x20 +413 ADD +414 PUSH1 0x00 +416 SHA3 +417 PUSH1 0x00 +419 DUP3 +420 DUP3 +421 SLOAD +422 SUB +423 SWAP3 +424 POP +425 POP +426 DUP2 +427 SWAP1 +428 SSTORE +429 POP +430 DUP2 +431 PUSH1 0x00 +433 DUP1 +434 DUP6 +435 PUSH20 0xffffffffffffffffffffffffffffffffffffffff +456 AND +457 PUSH20 0xffffffffffffffffffffffffffffffffffffffff +478 AND +479 DUP2 +480 MSTORE +481 PUSH1 0x20 +483 ADD +484 SWAP1 +485 DUP2 +486 MSTORE +487 PUSH1 0x20 +489 ADD +490 PUSH1 0x00 +492 SHA3 +493 PUSH1 0x00 +495 DUP3 +496 DUP3 +497 SLOAD +498 ADD +499 SWAP3 +500 POP +501 POP +502 DUP2 +503 SWAP1 +504 SSTORE +505 POP +506 PUSH1 0x00 +508 SWAP1 +509 POP +510 JUMPDEST +511 SWAP3 +512 SWAP2 +513 POP +514 POP +515 JUMP +516 STOP diff --git a/tests/testdata/outputs_expected/metacoin.sol.o.graph.html b/tests/testdata/outputs_expected/metacoin.sol.o.graph.html new file mode 100644 index 00000000..6ee81b9c --- /dev/null +++ b/tests/testdata/outputs_expected/metacoin.sol.o.graph.html @@ -0,0 +1,62 @@ + + + + Call Graph + + + + + + + + + + +

Mythril / Ethereum LASER Symbolic VM

+
+ + + diff --git a/tests/testdata/outputs_expected/metacoin.sol.o.json b/tests/testdata/outputs_expected/metacoin.sol.o.json index 4c976ff9..179ed787 100644 --- a/tests/testdata/outputs_expected/metacoin.sol.o.json +++ b/tests/testdata/outputs_expected/metacoin.sol.o.json @@ -1,18 +1,5 @@ { "error": null, - "issues": [] - { - "address": 498, - "contract": "Unknown", - "debug": "", - "description": "This binary add operation can result in integer overflow.\n", - "function": "sendToken(address,uint256)", - "max_gas_used": 27415, - "min_gas_used": 6750, - "swc-id": "101", - "title": "Integer Overflow", - "type": "Warning" - } - ], + "issues": [], "success": true } diff --git a/tests/testdata/outputs_expected/metacoin.sol.o.markdown b/tests/testdata/outputs_expected/metacoin.sol.o.markdown index a19876e5..321484fd 100644 --- a/tests/testdata/outputs_expected/metacoin.sol.o.markdown +++ b/tests/testdata/outputs_expected/metacoin.sol.o.markdown @@ -1,13 +1,3 @@ -# Analysis results for test-filename.sol +# Analysis results for None -## Integer Overflow -- SWC ID: 101 -- Type: Warning -- Contract: Unknown -- Function name: `sendToken(address,uint256)` -- PC address: 498 -- Estimated Gas Usage: 6750 - 27415 - -### Description - -This binary add operation can result in integer overflow. +The analysis was completed successfully. No issues were detected. diff --git a/tests/testdata/outputs_expected/metacoin.sol.o.text b/tests/testdata/outputs_expected/metacoin.sol.o.text index 8bcee7d5..729320d8 100644 --- a/tests/testdata/outputs_expected/metacoin.sol.o.text +++ b/tests/testdata/outputs_expected/metacoin.sol.o.text @@ -1,11 +1 @@ -==== Integer Overflow ==== -SWC ID: 101 -Type: Warning -Contract: Unknown -Function name: sendToken(address,uint256) -PC address: 498 -Estimated Gas Usage: 6750 - 27415 -This binary add operation can result in integer overflow. - --------------------- - +The analysis was completed successfully. No issues were detected. From 49a1f6fa251c7674b7a607167cda77fadf27c308 Mon Sep 17 00:00:00 2001 From: Joran Honig Date: Wed, 28 Nov 2018 12:39:04 +0100 Subject: [PATCH 24/41] mythril/laser/ethereum/state/calldata: Refactor to separate modules and re implement loading logic --- mythril/laser/ethereum/state/calldata.py | 154 +++++++++++++---------- tests/laser/state/calldata_test.py | 64 +++------- 2 files changed, 104 insertions(+), 114 deletions(-) diff --git a/mythril/laser/ethereum/state/calldata.py b/mythril/laser/ethereum/state/calldata.py index 1fd83316..6c587b46 100644 --- a/mythril/laser/ethereum/state/calldata.py +++ b/mythril/laser/ethereum/state/calldata.py @@ -3,13 +3,11 @@ from typing import Union, Any from z3 import ( BitVecVal, BitVecRef, - BitVecSort, BitVec, - Implies, simplify, Concat, - UGT, - Array, + If, + ExprRef ) from z3.z3types import Z3Exception @@ -21,84 +19,112 @@ class CalldataType(Enum): SYMBOLIC = 2 -class Calldata: - """ - Calldata class representing the calldata of a transaction - """ - - def __init__(self, tx_id, starting_calldata=None): - """ - Constructor for Calldata - :param tx_id: unique value representing the transaction the calldata is for - :param starting_calldata: byte array representing the concrete calldata of a transaction - """ +class BaseCalldata: + def __init__(self, tx_id): self.tx_id = tx_id - if starting_calldata is not None: - self._calldata = [] - self.calldatasize = BitVecVal(len(starting_calldata), 256) - self.concrete = True - else: - self._calldata = Array( - "{}_calldata".format(self.tx_id), BitVecSort(256), BitVecSort(8) - ) - self.calldatasize = BitVec("{}_calldatasize".format(self.tx_id), 256) - self.concrete = False - - if self.concrete: - for calldata_byte in starting_calldata: - if type(calldata_byte) == int: - self._calldata.append(BitVecVal(calldata_byte, 8)) - else: - self._calldata.append(calldata_byte) - - def concretized(self, model): - result = [] - for i in range( - get_concrete_int(model.eval(self.calldatasize, model_completion=True)) - ): - result.append( - get_concrete_int(model.eval(self._calldata[i], model_completion=True)) - ) + @property + def calldatasize(self): + result = self.size + if isinstance(result, int): + return BitVecVal(result, 256) return result def get_word_at(self, index: int): - return self[index : index + 32] + return self[index: index + 32] def __getitem__(self, item: Union[int, slice]) -> Any: + if isinstance(item, int): + return self._load(item) + if isinstance(item, slice): - start, step, stop = item.start, item.step, item.stop + start = 0 if item.start is None else item.start + step = 1 if item.step is None else item.step + stop = self.size if item.stop is None else item.stop + try: - if start is None: - start = 0 - if step is None: - step = 1 - if stop is None: - stop = self.calldatasize current_index = ( start if isinstance(start, BitVecRef) else BitVecVal(start, 256) ) - dataparts = [] + parts = [] while simplify(current_index != stop): - dataparts.append(self[current_index]) + parts.append(self._load(current_index)) current_index = simplify(current_index + step) except Z3Exception: raise IndexError("Invalid Calldata Slice") - values, constraints = zip(*dataparts) - result_constraints = [] - for c in constraints: - result_constraints.extend(c) - return simplify(Concat(values)), result_constraints + return simplify(Concat(parts)) + + raise ValueError + + def _load(self, item: Union[int, ExprRef]): + raise NotImplementedError() - if self.concrete: + @property + def size(self): + raise NotImplementedError() + + def concrete(self, model): + raise NotImplementedError + + +class ConcreteCalldata(BaseCalldata): + def __init__(self, tx_id, calldata): + self._calldata = calldata + super().__init__(tx_id) + + def _load(self, item: Union[int, ExprRef]) -> Any: + if isinstance(item, int): try: - return self._calldata[get_concrete_int(item)], () + return self._calldata[item] except IndexError: - return BitVecVal(0, 8), () - else: - constraints = [ - Implies(self._calldata[item] != 0, UGT(self.calldatasize, item)) - ] + return 0 + + value = BitVecVal(0x0, 8) + for i in range(self.size): + value = If(item == i, self._calldata[i], value) + return value + + def concrete(self, model): + return self._calldata + + @property + def size(self): + return len(self._calldata) + + +class SymbolicCalldata(BaseCalldata): + def __init__(self, tx_id): + self._reads = [] + self._size = BitVec("calldatasize", 256) + super().__init__(tx_id) + + def _load(self, item: Union[int, ExprRef], clean=False) -> Any: + x = BitVecVal(item, 256) if isinstance(item, int) else item + + b = BitVec("{}_calldata_{}".format(0, str(item)), 8) + c = x > self._size + a = BitVecVal(0, 8) + symbolic_base_value = If(c, a, b) + + return_value = symbolic_base_value + for r_index, r_value in self._reads: + return_value = If(r_index == item, r_value, return_value) + + if not clean: + self._reads.append((item, symbolic_base_value)) + return return_value + + def concrete(self, model): + concrete_length = get_concrete_int(model.eval(self.size, model_completion=True)) + result = [] + for i in range(concrete_length): + value = self._load(i, clean=True) + c_value = get_concrete_int(model.eval(value, model_completion=True)) + result.append(c_value) + + return result - return self._calldata[item], constraints + @property + def size(self): + return self._size diff --git a/tests/laser/state/calldata_test.py b/tests/laser/state/calldata_test.py index 9bce4a50..a2d09208 100644 --- a/tests/laser/state/calldata_test.py +++ b/tests/laser/state/calldata_test.py @@ -1,5 +1,5 @@ import pytest -from mythril.laser.ethereum.state.calldata import Calldata +from mythril.laser.ethereum.state.calldata import ConcreteCalldata, SymbolicCalldata from z3 import Solver, simplify from z3.z3types import Z3Exception from mock import MagicMock @@ -13,21 +13,11 @@ uninitialized_test_data = [ @pytest.mark.parametrize("starting_calldata", uninitialized_test_data) def test_concrete_calldata_uninitialized_index(starting_calldata): # Arrange - calldata = Calldata(0, starting_calldata) - solver = Solver() + calldata = ConcreteCalldata(0, starting_calldata) # Act - value, constraint1 = calldata[100] - value2, constraint2 = calldata.get_word_at(200) - - solver.add(constraint1) - solver.add(constraint2) - - solver.check() - model = solver.model() - - value = model.eval(value) - value2 = model.eval(value2) + value = calldata[100] + value2 = calldata.get_word_at(200) # Assert assert value == 0 @@ -36,71 +26,45 @@ def test_concrete_calldata_uninitialized_index(starting_calldata): def test_concrete_calldata_calldatasize(): # Arrange - calldata = Calldata(0, [1, 4, 7, 3, 7, 2, 9]) + calldata = ConcreteCalldata(0, [1, 4, 7, 3, 7, 2, 9]) solver = Solver() # Act solver.check() model = solver.model() - result = model.eval(calldata.calldatasize) # Assert assert result == 7 -def test_symbolic_calldata_constrain_index(): - # Arrange - calldata = Calldata(0) - solver = Solver() - - # Act - value, calldata_constraints = calldata[100] - constraint = value == 50 - - solver.add([constraint] + calldata_constraints) - - solver.check() - model = solver.model() - - value = model.eval(value) - calldatasize = model.eval(calldata.calldatasize) - - # Assert - assert value == 50 - assert simplify(calldatasize >= 100) - - def test_concrete_calldata_constrain_index(): # Arrange - calldata = Calldata(0, [1, 4, 7, 3, 7, 2, 9]) + calldata = ConcreteCalldata(0, [1, 4, 7, 3, 7, 2, 9]) solver = Solver() # Act - value, calldata_constraints = calldata[2] + value = calldata[2] constraint = value == 3 - solver.add([constraint] + calldata_constraints) + solver.add([constraint]) result = solver.check() # Assert assert str(result) == "unsat" -def test_concrete_calldata_constrain_index(): +def test_symbolic_calldata_constrain_index(): # Arrange - calldata = Calldata(0) - mstate = MagicMock() - mstate.constraints = [] + calldata = SymbolicCalldata(0) solver = Solver() # Act - constraints = [] - value, calldata_constraints = calldata[51] - constraints.append(value == 1) - constraints.append(calldata.calldatasize == 50) + value = calldata[51] + + constraints = [value == 1, calldata.calldatasize == 50] - solver.add(constraints + calldata_constraints) + solver.add(constraints) result = solver.check() From 3147d6ebd1f3120ba2faf9e195ea21fc0efa00f1 Mon Sep 17 00:00:00 2001 From: Joran Honig Date: Wed, 28 Nov 2018 12:48:44 +0100 Subject: [PATCH 25/41] mythril/laser: Refactor laser to support changes in calldata --- mythril/laser/ethereum/call.py | 6 +++--- mythril/laser/ethereum/instructions.py | 8 +++----- mythril/laser/ethereum/natives.py | 4 ++-- mythril/laser/ethereum/state/environment.py | 4 ++-- mythril/laser/ethereum/transaction/symbolic.py | 4 ++-- mythril/laser/ethereum/transaction/transaction_models.py | 6 +++--- 6 files changed, 15 insertions(+), 17 deletions(-) diff --git a/mythril/laser/ethereum/call.py b/mythril/laser/ethereum/call.py index cc567b89..d00c1719 100644 --- a/mythril/laser/ethereum/call.py +++ b/mythril/laser/ethereum/call.py @@ -3,7 +3,7 @@ from typing import Union from z3 import simplify, ExprRef, Extract import mythril.laser.ethereum.util as util from mythril.laser.ethereum.state.account import Account -from mythril.laser.ethereum.state.calldata import CalldataType, Calldata +from mythril.laser.ethereum.state.calldata import CalldataType, SymbolicCalldata, ConcreteCalldata from mythril.laser.ethereum.state.global_state import GlobalState from mythril.support.loader import DynLoader import re @@ -174,12 +174,12 @@ def get_call_data( starting_calldata.append(Extract(j + 7, j, elem)) i += 1 - call_data = Calldata(transaction_id, starting_calldata) + call_data = ConcreteCalldata(transaction_id, starting_calldata) call_data_type = CalldataType.CONCRETE logging.debug("Calldata: " + str(call_data)) except TypeError: logging.debug("Unsupported symbolic calldata offset") call_data_type = CalldataType.SYMBOLIC - call_data = Calldata("{}_internalcall".format(transaction_id)) + call_data = SymbolicCalldata("{}_internalcall".format(transaction_id)) return call_data, call_data_type diff --git a/mythril/laser/ethereum/instructions.py b/mythril/laser/ethereum/instructions.py index 7ade087d..005244e2 100644 --- a/mythril/laser/ethereum/instructions.py +++ b/mythril/laser/ethereum/instructions.py @@ -42,7 +42,7 @@ from mythril.laser.ethereum.evm_exceptions import ( ) from mythril.laser.ethereum.gas import OPCODE_GAS from mythril.laser.ethereum.keccak import KeccakFunctionManager -from mythril.laser.ethereum.state.calldata import CalldataType, Calldata +from mythril.laser.ethereum.state.calldata import CalldataType from mythril.laser.ethereum.state.global_state import GlobalState from mythril.laser.ethereum.transaction import ( MessageCallTransaction, @@ -458,10 +458,9 @@ class Instruction: environment = global_state.environment op0 = state.stack.pop() - value, constraints = environment.calldata.get_word_at(op0) + value = environment.calldata.get_word_at(op0) state.stack.append(value) - state.constraints.extend(constraints) return [global_state] @@ -541,9 +540,8 @@ class Instruction: i_data = dstart new_memory = [] for i in range(size): - value, constraints = environment.calldata[i_data] + value = environment.calldata[i_data] new_memory.append(value) - state.constraints.extend(constraints) i_data = ( i_data + 1 if isinstance(i_data, int) else simplify(i_data + 1) diff --git a/mythril/laser/ethereum/natives.py b/mythril/laser/ethereum/natives.py index ed8a0c4c..f6293831 100644 --- a/mythril/laser/ethereum/natives.py +++ b/mythril/laser/ethereum/natives.py @@ -8,7 +8,7 @@ from ethereum.utils import ecrecover_to_pub from py_ecc.secp256k1 import N as secp256k1n from rlp.utils import ALL_BYTES -from mythril.laser.ethereum.state.calldata import Calldata +from mythril.laser.ethereum.state.calldata import BaseCalldata from mythril.laser.ethereum.util import bytearray_to_int, sha3, get_concrete_int from z3 import Concat, simplify @@ -88,7 +88,7 @@ def identity(data: Union[bytes, str, List[int]]) -> bytes: return result -def native_contracts(address: int, data: Calldata): +def native_contracts(address: int, data: BaseCalldata): """ takes integer address 1, 2, 3, 4 """ diff --git a/mythril/laser/ethereum/state/environment.py b/mythril/laser/ethereum/state/environment.py index 91c67d5a..a54e5c42 100644 --- a/mythril/laser/ethereum/state/environment.py +++ b/mythril/laser/ethereum/state/environment.py @@ -3,7 +3,7 @@ from typing import Dict from z3 import ExprRef, BitVecVal from mythril.laser.ethereum.state.account import Account -from mythril.laser.ethereum.state.calldata import Calldata, CalldataType +from mythril.laser.ethereum.state.calldata import CalldataType, BaseCalldata class Environment: @@ -15,7 +15,7 @@ class Environment: self, active_account: Account, sender: ExprRef, - calldata: Calldata, + calldata: BaseCalldata, gasprice: ExprRef, callvalue: ExprRef, origin: ExprRef, diff --git a/mythril/laser/ethereum/transaction/symbolic.py b/mythril/laser/ethereum/transaction/symbolic.py index 88e16780..53e195eb 100644 --- a/mythril/laser/ethereum/transaction/symbolic.py +++ b/mythril/laser/ethereum/transaction/symbolic.py @@ -3,7 +3,7 @@ from logging import debug from mythril.disassembler.disassembly import Disassembly from mythril.laser.ethereum.cfg import Node, Edge, JumpType -from mythril.laser.ethereum.state.calldata import CalldataType, Calldata +from mythril.laser.ethereum.state.calldata import CalldataType, BaseCalldata, SymbolicCalldata from mythril.laser.ethereum.state.account import Account from mythril.laser.ethereum.transaction.transaction_models import ( MessageCallTransaction, @@ -32,7 +32,7 @@ def execute_message_call(laser_evm, callee_address: str) -> None: origin=BitVec("origin{}".format(next_transaction_id), 256), caller=BitVec("caller{}".format(next_transaction_id), 256), callee_account=open_world_state[callee_address], - call_data=Calldata(next_transaction_id), + call_data=SymbolicCalldata(next_transaction_id), call_data_type=CalldataType.SYMBOLIC, call_value=BitVec("call_value{}".format(next_transaction_id), 256), ) diff --git a/mythril/laser/ethereum/transaction/transaction_models.py b/mythril/laser/ethereum/transaction/transaction_models.py index bef83a2a..01cc71b5 100644 --- a/mythril/laser/ethereum/transaction/transaction_models.py +++ b/mythril/laser/ethereum/transaction/transaction_models.py @@ -2,7 +2,7 @@ import logging from typing import Union from mythril.disassembler.disassembly import Disassembly from mythril.laser.ethereum.state.environment import Environment -from mythril.laser.ethereum.state.calldata import Calldata +from mythril.laser.ethereum.state.calldata import BaseCalldata, ConcreteCalldata, SymbolicCalldata from mythril.laser.ethereum.state.account import Account from mythril.laser.ethereum.state.world_state import WorldState from mythril.laser.ethereum.state.global_state import GlobalState @@ -75,9 +75,9 @@ class BaseTransaction: self.caller = caller self.callee_account = callee_account if call_data is None and init_call_data: - self.call_data = Calldata(self.id, call_data) + self.call_data = ConcreteCalldata(self.id, call_data) else: - self.call_data = call_data if isinstance(call_data, Calldata) else None + self.call_data = call_data if isinstance(call_data, BaseCalldata) else None self.call_data_type = ( call_data_type if call_data_type is not None From 8ebe9a855de4e91ab69989aa3d8a179de9f21ec2 Mon Sep 17 00:00:00 2001 From: Joran Honig Date: Wed, 28 Nov 2018 12:48:59 +0100 Subject: [PATCH 26/41] mythril/analysis/solver: Refactor to support changes in calldata --- mythril/analysis/solver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mythril/analysis/solver.py b/mythril/analysis/solver.py index 2848f205..b91cc498 100644 --- a/mythril/analysis/solver.py +++ b/mythril/analysis/solver.py @@ -103,7 +103,7 @@ def get_transaction_sequence(global_state, constraints): concrete_transactions[tx_id]["calldata"] = "0x" + "".join( [ hex(b)[2:] if len(hex(b)) % 2 == 0 else "0" + hex(b)[2:] - for b in transaction.call_data.concretized(model) + for b in transaction.call_data.concrete(model) ] ) From 7ead0dd1ac185b046822ec1a257af4b156c5cfb5 Mon Sep 17 00:00:00 2001 From: Joran Honig Date: Wed, 28 Nov 2018 12:50:34 +0100 Subject: [PATCH 27/41] mythril/laser/ethereum: style changes --- mythril/laser/ethereum/call.py | 6 +++++- mythril/laser/ethereum/state/calldata.py | 12 ++---------- mythril/laser/ethereum/transaction/symbolic.py | 6 +++++- .../laser/ethereum/transaction/transaction_models.py | 6 +++++- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/mythril/laser/ethereum/call.py b/mythril/laser/ethereum/call.py index d00c1719..de14d42e 100644 --- a/mythril/laser/ethereum/call.py +++ b/mythril/laser/ethereum/call.py @@ -3,7 +3,11 @@ from typing import Union from z3 import simplify, ExprRef, Extract import mythril.laser.ethereum.util as util from mythril.laser.ethereum.state.account import Account -from mythril.laser.ethereum.state.calldata import CalldataType, SymbolicCalldata, ConcreteCalldata +from mythril.laser.ethereum.state.calldata import ( + CalldataType, + SymbolicCalldata, + ConcreteCalldata, +) from mythril.laser.ethereum.state.global_state import GlobalState from mythril.support.loader import DynLoader import re diff --git a/mythril/laser/ethereum/state/calldata.py b/mythril/laser/ethereum/state/calldata.py index 6c587b46..4b885310 100644 --- a/mythril/laser/ethereum/state/calldata.py +++ b/mythril/laser/ethereum/state/calldata.py @@ -1,14 +1,6 @@ from enum import Enum from typing import Union, Any -from z3 import ( - BitVecVal, - BitVecRef, - BitVec, - simplify, - Concat, - If, - ExprRef -) +from z3 import BitVecVal, BitVecRef, BitVec, simplify, Concat, If, ExprRef from z3.z3types import Z3Exception from mythril.laser.ethereum.util import get_concrete_int @@ -31,7 +23,7 @@ class BaseCalldata: return result def get_word_at(self, index: int): - return self[index: index + 32] + return self[index : index + 32] def __getitem__(self, item: Union[int, slice]) -> Any: if isinstance(item, int): diff --git a/mythril/laser/ethereum/transaction/symbolic.py b/mythril/laser/ethereum/transaction/symbolic.py index 53e195eb..be1c0ec6 100644 --- a/mythril/laser/ethereum/transaction/symbolic.py +++ b/mythril/laser/ethereum/transaction/symbolic.py @@ -3,7 +3,11 @@ from logging import debug from mythril.disassembler.disassembly import Disassembly from mythril.laser.ethereum.cfg import Node, Edge, JumpType -from mythril.laser.ethereum.state.calldata import CalldataType, BaseCalldata, SymbolicCalldata +from mythril.laser.ethereum.state.calldata import ( + CalldataType, + BaseCalldata, + SymbolicCalldata, +) from mythril.laser.ethereum.state.account import Account from mythril.laser.ethereum.transaction.transaction_models import ( MessageCallTransaction, diff --git a/mythril/laser/ethereum/transaction/transaction_models.py b/mythril/laser/ethereum/transaction/transaction_models.py index 01cc71b5..7fe73328 100644 --- a/mythril/laser/ethereum/transaction/transaction_models.py +++ b/mythril/laser/ethereum/transaction/transaction_models.py @@ -2,7 +2,11 @@ import logging from typing import Union from mythril.disassembler.disassembly import Disassembly from mythril.laser.ethereum.state.environment import Environment -from mythril.laser.ethereum.state.calldata import BaseCalldata, ConcreteCalldata, SymbolicCalldata +from mythril.laser.ethereum.state.calldata import ( + BaseCalldata, + ConcreteCalldata, + SymbolicCalldata, +) from mythril.laser.ethereum.state.account import Account from mythril.laser.ethereum.state.world_state import WorldState from mythril.laser.ethereum.state.global_state import GlobalState From 8013e571a8c427e594a01867ad16329f5a6d4be3 Mon Sep 17 00:00:00 2001 From: Joran Honig Date: Wed, 28 Nov 2018 12:53:34 +0100 Subject: [PATCH 28/41] mythril/laser/ethereum: fix broken import --- mythril/laser/ethereum/transaction/concolic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mythril/laser/ethereum/transaction/concolic.py b/mythril/laser/ethereum/transaction/concolic.py index 0ec88794..6c7a406d 100644 --- a/mythril/laser/ethereum/transaction/concolic.py +++ b/mythril/laser/ethereum/transaction/concolic.py @@ -6,7 +6,7 @@ from mythril.laser.ethereum.transaction.transaction_models import ( ) from z3 import BitVec from mythril.laser.ethereum.state.environment import Environment -from mythril.laser.ethereum.state.calldata import Calldata, CalldataType +from mythril.laser.ethereum.state.calldata import CalldataType, ConcreteCalldata from mythril.laser.ethereum.state.account import Account from mythril.laser.ethereum.state.world_state import WorldState from mythril.laser.ethereum.state.global_state import GlobalState @@ -42,7 +42,7 @@ def execute_message_call( code=Disassembly(code), caller=caller_address, callee_account=open_world_state[callee_address], - call_data=Calldata(next_transaction_id, data), + call_data=ConcreteCalldata(next_transaction_id, data), call_data_type=CalldataType.SYMBOLIC, call_value=value, ) From c407955d8fc50afc552b39c38828f0c28047ade0 Mon Sep 17 00:00:00 2001 From: Joran Honig Date: Wed, 28 Nov 2018 13:34:17 +0100 Subject: [PATCH 29/41] mythril/laser/ethereum/state/calldata: make symbol creation unique --- mythril/laser/ethereum/state/calldata.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mythril/laser/ethereum/state/calldata.py b/mythril/laser/ethereum/state/calldata.py index 4b885310..3d144b8c 100644 --- a/mythril/laser/ethereum/state/calldata.py +++ b/mythril/laser/ethereum/state/calldata.py @@ -94,7 +94,7 @@ class SymbolicCalldata(BaseCalldata): def _load(self, item: Union[int, ExprRef], clean=False) -> Any: x = BitVecVal(item, 256) if isinstance(item, int) else item - b = BitVec("{}_calldata_{}".format(0, str(item)), 8) + b = BitVec("{}_calldata_{}".format(self.tx_id, str(item)), 8) c = x > self._size a = BitVecVal(0, 8) symbolic_base_value = If(c, a, b) @@ -103,6 +103,8 @@ class SymbolicCalldata(BaseCalldata): for r_index, r_value in self._reads: return_value = If(r_index == item, r_value, return_value) + return_value = simplify(return_value) + if not clean: self._reads.append((item, symbolic_base_value)) return return_value From 22557819e7e15bf492b22a51f6e554bed8507c75 Mon Sep 17 00:00:00 2001 From: Joran Honig Date: Wed, 28 Nov 2018 13:34:45 +0100 Subject: [PATCH 30/41] mythril/analysis/modules/integer: Remove false positive regex checks --- mythril/analysis/modules/integer.py | 18 ++---------------- tests/testdata/input_contracts/overflow.sol | 2 +- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/mythril/analysis/modules/integer.py b/mythril/analysis/modules/integer.py index 9bd354a7..515a0e85 100644 --- a/mythril/analysis/modules/integer.py +++ b/mythril/analysis/modules/integer.py @@ -167,33 +167,19 @@ class IntegerOverflowUnderflowModule(DetectionModule): constraints = copy.deepcopy(node.constraints) - # Filter for patterns that indicate benign underflows - - # Pattern 1: (96 + calldatasize_MAIN) - (96), where (96 + calldatasize_MAIN) would underflow if calldatasize is very large. - # Pattern 2: (256*If(1 & storage_0 == 0, 1, 0)) - 1, this would underlow if storage_0 = 0 if type(op0) == int and type(op1) == int: return [] - if re.search(r"calldatasize_", str(op0)): - return [] - if re.search(r"256\*.*If\(1", str(op0), re.DOTALL) or re.search( - r"256\*.*If\(1", str(op1), re.DOTALL - ): - return [] - if re.search(r"32 \+.*calldata", str(op0), re.DOTALL) or re.search( - r"32 \+.*calldata", str(op1), re.DOTALL - ): - return [] logging.debug( "[INTEGER_UNDERFLOW] Checking SUB {0}, {1} at address {2}".format( str(op0), str(op1), str(instruction["address"]) ) ) + allowed_types = [int, BitVecRef, BitVecNumRef] if type(op0) in allowed_types and type(op1) in allowed_types: - constraints.append(UGT(op1, op0)) - + constraints.append(Not(BVSubNoUnderflow(op0, op1, signed=False))) try: model = solver.get_model(constraints) diff --git a/tests/testdata/input_contracts/overflow.sol b/tests/testdata/input_contracts/overflow.sol index d1d3d875..d434b917 100644 --- a/tests/testdata/input_contracts/overflow.sol +++ b/tests/testdata/input_contracts/overflow.sol @@ -11,7 +11,7 @@ contract Over { } function sendeth(address _to, uint _value) public returns (bool) { - require(balances[msg.sender] - _value >= 0); + // require(balances[msg.sender] - _value >= 0); balances[msg.sender] -= _value; balances[_to] += _value; return true; From 91617c25b9418f2ced7dda9acb460e1b75f47f93 Mon Sep 17 00:00:00 2001 From: Joran Honig Date: Wed, 28 Nov 2018 14:35:37 +0100 Subject: [PATCH 31/41] mythril/laser/ethereum/state/calldata: Add documentation and type hints --- mythril/laser/ethereum/state/calldata.py | 58 ++++++++++++++++-------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/mythril/laser/ethereum/state/calldata.py b/mythril/laser/ethereum/state/calldata.py index 3d144b8c..1fc93e39 100644 --- a/mythril/laser/ethereum/state/calldata.py +++ b/mythril/laser/ethereum/state/calldata.py @@ -1,7 +1,7 @@ from enum import Enum from typing import Union, Any from z3 import BitVecVal, BitVecRef, BitVec, simplify, Concat, If, ExprRef -from z3.z3types import Z3Exception +from z3.z3types import Z3Exception, Model from mythril.laser.ethereum.util import get_concrete_int @@ -12,18 +12,26 @@ class CalldataType(Enum): class BaseCalldata: + """ + Base calldata class + This represents the calldata provided when sending a transaction to a contract + """ def __init__(self, tx_id): self.tx_id = tx_id @property - def calldatasize(self): + def calldatasize(self) -> ExprRef: + """ + :return: Calldata size for this calldata object + """ result = self.size if isinstance(result, int): return BitVecVal(result, 256) return result - def get_word_at(self, index: int): - return self[index : index + 32] + def get_word_at(self, offset: int) -> ExprRef: + """ Gets word at offset""" + return self[offset: offset + 32] def __getitem__(self, item: Union[int, slice]) -> Any: if isinstance(item, int): @@ -49,19 +57,26 @@ class BaseCalldata: raise ValueError - def _load(self, item: Union[int, ExprRef]): + def _load(self, item: Union[int, ExprRef]) -> Any: raise NotImplementedError() @property - def size(self): + def size(self) -> Union[ExprRef, int]: + """ Returns the exact size of this calldata, this is not normalized""" raise NotImplementedError() - def concrete(self, model): + def concrete(self, model: Model) -> list: + """ Returns a concrete version of the calldata using the provided model""" raise NotImplementedError class ConcreteCalldata(BaseCalldata): - def __init__(self, tx_id, calldata): + def __init__(self, tx_id: int, calldata: list): + """ + Initializes the ConcreteCalldata object + :param tx_id: Id of the transaction that the calldata is for. + :param calldata: The concrete calldata content + """ self._calldata = calldata super().__init__(tx_id) @@ -77,16 +92,20 @@ class ConcreteCalldata(BaseCalldata): value = If(item == i, self._calldata[i], value) return value - def concrete(self, model): + def concrete(self, model: Model) -> list: return self._calldata @property - def size(self): + def size(self) -> int: return len(self._calldata) class SymbolicCalldata(BaseCalldata): - def __init__(self, tx_id): + def __init__(self, tx_id: int): + """ + Initializes the SymbolicCalldata object + :param tx_id: Id of the transaction that the calldata is for. + """ self._reads = [] self._size = BitVec("calldatasize", 256) super().__init__(tx_id) @@ -94,22 +113,21 @@ class SymbolicCalldata(BaseCalldata): def _load(self, item: Union[int, ExprRef], clean=False) -> Any: x = BitVecVal(item, 256) if isinstance(item, int) else item - b = BitVec("{}_calldata_{}".format(self.tx_id, str(item)), 8) - c = x > self._size - a = BitVecVal(0, 8) - symbolic_base_value = If(c, a, b) + symbolic_base_value = If( + x > self._size, + BitVecVal(0, 8), + BitVec("{}_calldata_{}".format(self.tx_id, str(item)), 8) + ) return_value = symbolic_base_value for r_index, r_value in self._reads: return_value = If(r_index == item, r_value, return_value) - return_value = simplify(return_value) - if not clean: self._reads.append((item, symbolic_base_value)) - return return_value + return simplify(return_value) - def concrete(self, model): + def concrete(self, model: Model) -> list: concrete_length = get_concrete_int(model.eval(self.size, model_completion=True)) result = [] for i in range(concrete_length): @@ -120,5 +138,5 @@ class SymbolicCalldata(BaseCalldata): return result @property - def size(self): + def size(self) -> ExprRef: return self._size From 2c11246bb626404d76cb84fe0a1a741191ef3fb1 Mon Sep 17 00:00:00 2001 From: Joran Honig Date: Wed, 28 Nov 2018 14:42:06 +0100 Subject: [PATCH 32/41] add symbolic calldata indices test --- tests/laser/state/calldata_test.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/laser/state/calldata_test.py b/tests/laser/state/calldata_test.py index a2d09208..3c013f76 100644 --- a/tests/laser/state/calldata_test.py +++ b/tests/laser/state/calldata_test.py @@ -1,6 +1,6 @@ import pytest from mythril.laser.ethereum.state.calldata import ConcreteCalldata, SymbolicCalldata -from z3 import Solver, simplify +from z3 import Solver, simplify, BitVec, sat, unsat from z3.z3types import Z3Exception from mock import MagicMock @@ -70,3 +70,21 @@ def test_symbolic_calldata_constrain_index(): # Assert assert str(result) == "unsat" + + +def test_symbolic_calldata_equal_indices(): + calldata = SymbolicCalldata(0) + + index_a = BitVec("index_a", 256) + index_b = BitVec("index_b", 256) + + # Act + a = calldata[index_a] + b = calldata[index_b] + + s = Solver() + s.append(index_a == index_b) + s.append(a != b) + + # Assert + assert unsat == s.check() From 733439508743c2cbec70334b973470c8b6cc2a3d Mon Sep 17 00:00:00 2001 From: Joran Honig Date: Wed, 28 Nov 2018 14:42:19 +0100 Subject: [PATCH 33/41] Handle ExprRef input --- mythril/laser/ethereum/state/calldata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mythril/laser/ethereum/state/calldata.py b/mythril/laser/ethereum/state/calldata.py index 1fc93e39..a14af8d5 100644 --- a/mythril/laser/ethereum/state/calldata.py +++ b/mythril/laser/ethereum/state/calldata.py @@ -34,7 +34,7 @@ class BaseCalldata: return self[offset: offset + 32] def __getitem__(self, item: Union[int, slice]) -> Any: - if isinstance(item, int): + if isinstance(item, int) or isinstance(item, ExprRef): return self._load(item) if isinstance(item, slice): From eb7433a830c0fffedacd87739eb89d2512980f62 Mon Sep 17 00:00:00 2001 From: Joran Honig Date: Wed, 28 Nov 2018 15:03:58 +0100 Subject: [PATCH 34/41] Adaption to calldata style --- mythril/laser/ethereum/state/calldata.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mythril/laser/ethereum/state/calldata.py b/mythril/laser/ethereum/state/calldata.py index a14af8d5..3cbbf834 100644 --- a/mythril/laser/ethereum/state/calldata.py +++ b/mythril/laser/ethereum/state/calldata.py @@ -16,6 +16,7 @@ class BaseCalldata: Base calldata class This represents the calldata provided when sending a transaction to a contract """ + def __init__(self, tx_id): self.tx_id = tx_id @@ -31,7 +32,7 @@ class BaseCalldata: def get_word_at(self, offset: int) -> ExprRef: """ Gets word at offset""" - return self[offset: offset + 32] + return self[offset : offset + 32] def __getitem__(self, item: Union[int, slice]) -> Any: if isinstance(item, int) or isinstance(item, ExprRef): @@ -116,7 +117,7 @@ class SymbolicCalldata(BaseCalldata): symbolic_base_value = If( x > self._size, BitVecVal(0, 8), - BitVec("{}_calldata_{}".format(self.tx_id, str(item)), 8) + BitVec("{}_calldata_{}".format(self.tx_id, str(item)), 8), ) return_value = symbolic_base_value From 5c70bdcf9499d59b6f866891c49f81c02e0faad3 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Thu, 29 Nov 2018 11:29:06 +0530 Subject: [PATCH 35/41] Copy the constraints --- mythril/analysis/modules/ether_thief.py | 5 ++--- mythril/support/support_utils.py | 0 2 files changed, 2 insertions(+), 3 deletions(-) create mode 100644 mythril/support/support_utils.py diff --git a/mythril/analysis/modules/ether_thief.py b/mythril/analysis/modules/ether_thief.py index 387377c1..292e2896 100644 --- a/mythril/analysis/modules/ether_thief.py +++ b/mythril/analysis/modules/ether_thief.py @@ -7,7 +7,7 @@ from mythril.laser.ethereum.state.global_state import GlobalState from mythril.exceptions import UnsatError from z3 import BitVecVal, UGT, Sum import logging - +from copy import copy DESCRIPTION = """ @@ -35,7 +35,7 @@ def _analyze_state(state): eth_sent_total = BitVecVal(0, 256) - constraints = node.constraints + constraints = copy(node.constraints) for tx in state.world_state.transaction_sequence: if tx.caller == 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF: @@ -45,7 +45,6 @@ def _analyze_state(state): constraints += [BVAddNoOverflow(eth_sent_total, tx.call_value, False)] eth_sent_total = Sum(eth_sent_total, tx.call_value) - constraints += [UGT(call_value, eth_sent_total), target == state.environment.sender] try: diff --git a/mythril/support/support_utils.py b/mythril/support/support_utils.py new file mode 100644 index 00000000..e69de29b From 1c3ea80874c07a1da8d89c4d1a2c99e432a8c112 Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Thu, 29 Nov 2018 15:20:48 +0700 Subject: [PATCH 36/41] Update tests --- .../outputs_expected/calls.sol.o.json | 67 +------------------ .../outputs_expected/calls.sol.o.markdown | 42 +++++++++++- .../outputs_expected/calls.sol.o.text | 36 +++++++++- .../kinds_of_calls.sol.o.json | 55 +-------------- .../kinds_of_calls.sol.o.markdown | 4 +- .../kinds_of_calls.sol.o.text | 4 +- .../outputs_expected/returnvalue.sol.o.json | 19 +----- .../returnvalue.sol.o.markdown | 24 +++++++ .../outputs_expected/returnvalue.sol.o.text | 20 ++++++ 9 files changed, 123 insertions(+), 148 deletions(-) diff --git a/tests/testdata/outputs_expected/calls.sol.o.json b/tests/testdata/outputs_expected/calls.sol.o.json index 6d4d8063..d8b68faa 100644 --- a/tests/testdata/outputs_expected/calls.sol.o.json +++ b/tests/testdata/outputs_expected/calls.sol.o.json @@ -1,66 +1 @@ -{ - "error": null, - "issues": [ - { - "address": 666, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "thisisfine()", - "max_gas_used": 35963, - "min_gas_used": 1352, - "swc-id": "104", - "title": "Unchecked CALL return value", - "type": "Informational" - }, - { - "address": 784, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "callstoredaddress()", - "max_gas_used": 36007, - "min_gas_used": 1396, - "swc-id": "104", - "title": "Unchecked CALL return value", - "type": "Informational" - }, - { - "address": 871, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "_function_0xe11f493e", - "max_gas_used": 61043, - "min_gas_used": 6432, - "swc-id": "104", - "title": "Unchecked CALL return value", - "type": "Informational" - }, - { - "address": 912, - "contract": "Unknown", - "debug": "", - "description": "This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.", - "function": "calluseraddress(address)", - "max_gas_used": 616, - "min_gas_used": 335, - "swc-id": "107", - "title": "Message call to external contract", - "type": "Warning" - }, - { - "address": 918, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "calluseraddress(address)", - "max_gas_used": 35327, - "min_gas_used": 1046, - "swc-id": "104", - "title": "Unchecked CALL return value", - "type": "Informational" - } - ], - "success": true -} +{"error": null, "issues": [{"address": 661, "contract": "Unknown", "debug": "", "description": "The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable.", "function": "thisisfine()", "max_gas_used": 1254, "min_gas_used": 643, "swc-id": "107", "title": "External call", "type": "Informational"}, {"address": 666, "contract": "Unknown", "debug": "", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "thisisfine()", "max_gas_used": 35963, "min_gas_used": 1352, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}, {"address": 779, "contract": "Unknown", "debug": "", "description": "The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable.", "function": "callstoredaddress()", "max_gas_used": 1298, "min_gas_used": 687, "swc-id": "107", "title": "External call", "type": "Informational"}, {"address": 784, "contract": "Unknown", "debug": "", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "callstoredaddress()", "max_gas_used": 36007, "min_gas_used": 1396, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}, {"address": 858, "contract": "Unknown", "debug": "", "description": "The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable.", "function": "reentrancy()", "max_gas_used": 1320, "min_gas_used": 709, "swc-id": "107", "title": "External call", "type": "Informational"}, {"address": 871, "contract": "Unknown", "debug": "", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "reentrancy()", "max_gas_used": 61043, "min_gas_used": 6432, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}, {"address": 912, "contract": "Unknown", "debug": "", "description": "The contract executes a function call with high gas to a user-supplied address. Note that the callee can contain arbitrary code and may re-enter any function in this contract. Review the business logic carefully to prevent unanticipated effects on the contract state.", "function": "calluseraddress(address)", "max_gas_used": 616, "min_gas_used": 335, "swc-id": "107", "title": "External call to user-supplied address", "type": "Warning"}, {"address": 918, "contract": "Unknown", "debug": "", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "calluseraddress(address)", "max_gas_used": 35327, "min_gas_used": 1046, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}], "success": true} \ No newline at end of file diff --git a/tests/testdata/outputs_expected/calls.sol.o.markdown b/tests/testdata/outputs_expected/calls.sol.o.markdown index 297047d2..ff656859 100644 --- a/tests/testdata/outputs_expected/calls.sol.o.markdown +++ b/tests/testdata/outputs_expected/calls.sol.o.markdown @@ -1,5 +1,17 @@ # Analysis results for test-filename.sol +## External call +- SWC ID: 107 +- Type: Informational +- Contract: Unknown +- Function name: `thisisfine()` +- PC address: 661 +- Estimated Gas Usage: 643 - 1254 + +### Description + +The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable. + ## Unchecked CALL return value - SWC ID: 104 - Type: Informational @@ -12,6 +24,18 @@ The return value of an external call is not checked. Note that execution continue even if the called contract throws. +## External call +- SWC ID: 107 +- Type: Informational +- Contract: Unknown +- Function name: `callstoredaddress()` +- PC address: 779 +- Estimated Gas Usage: 687 - 1298 + +### Description + +The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable. + ## Unchecked CALL return value - SWC ID: 104 - Type: Informational @@ -24,11 +48,23 @@ The return value of an external call is not checked. Note that execution continu The return value of an external call is not checked. Note that execution continue even if the called contract throws. +## External call +- SWC ID: 107 +- Type: Informational +- Contract: Unknown +- Function name: `reentrancy()` +- PC address: 858 +- Estimated Gas Usage: 709 - 1320 + +### Description + +The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable. + ## Unchecked CALL return value - SWC ID: 104 - Type: Informational - Contract: Unknown -- Function name: `_function_0xe11f493e` +- Function name: `reentrancy()` - PC address: 871 - Estimated Gas Usage: 6432 - 61043 @@ -36,7 +72,7 @@ The return value of an external call is not checked. Note that execution continu The return value of an external call is not checked. Note that execution continue even if the called contract throws. -## Message call to external contract +## External call to user-supplied address - SWC ID: 107 - Type: Warning - Contract: Unknown @@ -46,7 +82,7 @@ The return value of an external call is not checked. Note that execution continu ### Description -This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state. +The contract executes a function call with high gas to a user-supplied address. Note that the callee can contain arbitrary code and may re-enter any function in this contract. Review the business logic carefully to prevent unanticipated effects on the contract state. ## Unchecked CALL return value - SWC ID: 104 diff --git a/tests/testdata/outputs_expected/calls.sol.o.text b/tests/testdata/outputs_expected/calls.sol.o.text index 02407bee..1cbfb35e 100644 --- a/tests/testdata/outputs_expected/calls.sol.o.text +++ b/tests/testdata/outputs_expected/calls.sol.o.text @@ -1,3 +1,13 @@ +==== External call ==== +SWC ID: 107 +Type: Informational +Contract: Unknown +Function name: thisisfine() +PC address: 661 +Estimated Gas Usage: 643 - 1254 +The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable. +-------------------- + ==== Unchecked CALL return value ==== SWC ID: 104 Type: Informational @@ -8,6 +18,16 @@ Estimated Gas Usage: 1352 - 35963 The return value of an external call is not checked. Note that execution continue even if the called contract throws. -------------------- +==== External call ==== +SWC ID: 107 +Type: Informational +Contract: Unknown +Function name: callstoredaddress() +PC address: 779 +Estimated Gas Usage: 687 - 1298 +The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable. +-------------------- + ==== Unchecked CALL return value ==== SWC ID: 104 Type: Informational @@ -18,24 +38,34 @@ Estimated Gas Usage: 1396 - 36007 The return value of an external call is not checked. Note that execution continue even if the called contract throws. -------------------- +==== External call ==== +SWC ID: 107 +Type: Informational +Contract: Unknown +Function name: reentrancy() +PC address: 858 +Estimated Gas Usage: 709 - 1320 +The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable. +-------------------- + ==== Unchecked CALL return value ==== SWC ID: 104 Type: Informational Contract: Unknown -Function name: _function_0xe11f493e +Function name: reentrancy() PC address: 871 Estimated Gas Usage: 6432 - 61043 The return value of an external call is not checked. Note that execution continue even if the called contract throws. -------------------- -==== Message call to external contract ==== +==== External call to user-supplied address ==== SWC ID: 107 Type: Warning Contract: Unknown Function name: calluseraddress(address) PC address: 912 Estimated Gas Usage: 335 - 616 -This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state. +The contract executes a function call with high gas to a user-supplied address. Note that the callee can contain arbitrary code and may re-enter any function in this contract. Review the business logic carefully to prevent unanticipated effects on the contract state. -------------------- ==== Unchecked CALL return value ==== diff --git a/tests/testdata/outputs_expected/kinds_of_calls.sol.o.json b/tests/testdata/outputs_expected/kinds_of_calls.sol.o.json index a373f020..33da5788 100644 --- a/tests/testdata/outputs_expected/kinds_of_calls.sol.o.json +++ b/tests/testdata/outputs_expected/kinds_of_calls.sol.o.json @@ -1,54 +1 @@ -{ - "error": null, - "issues": [ - { - "address": 626, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "_function_0x141f32ff", - "max_gas_used": 35856, - "min_gas_used": 1104, - "swc-id": "104", - "title": "Unchecked CALL return value", - "type": "Informational" - }, - { - "address": 857, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "_function_0x9b58bc26", - "max_gas_used": 35919, - "min_gas_used": 1167, - "swc-id": "104", - "title": "Unchecked CALL return value", - "type": "Informational" - }, - { - "address": 1038, - "contract": "Unknown", - "debug": "", - "description": "This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.", - "function": "_function_0xeea4c864", - "max_gas_used": 1229, - "min_gas_used": 477, - "swc-id": "107", - "title": "Message call to external contract", - "type": "Warning" - }, - { - "address": 1046, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "_function_0xeea4c864", - "max_gas_used": 35944, - "min_gas_used": 1192, - "swc-id": "104", - "title": "Unchecked CALL return value", - "type": "Informational" - } - ], - "success": true -} +{"error": null, "issues": [{"address": 626, "contract": "Unknown", "debug": "", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "_function_0x141f32ff", "max_gas_used": 35856, "min_gas_used": 1104, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}, {"address": 857, "contract": "Unknown", "debug": "", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "_function_0x9b58bc26", "max_gas_used": 35919, "min_gas_used": 1167, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}, {"address": 1038, "contract": "Unknown", "debug": "", "description": "The contract executes a function call with high gas to a user-supplied address. Note that the callee can contain arbitrary code and may re-enter any function in this contract. Review the business logic carefully to prevent unanticipated effects on the contract state.", "function": "_function_0xeea4c864", "max_gas_used": 1229, "min_gas_used": 477, "swc-id": "107", "title": "External call to user-supplied address", "type": "Warning"}, {"address": 1046, "contract": "Unknown", "debug": "", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "_function_0xeea4c864", "max_gas_used": 35944, "min_gas_used": 1192, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}], "success": true} \ No newline at end of file diff --git a/tests/testdata/outputs_expected/kinds_of_calls.sol.o.markdown b/tests/testdata/outputs_expected/kinds_of_calls.sol.o.markdown index 98f6e0b2..d3fb63bf 100644 --- a/tests/testdata/outputs_expected/kinds_of_calls.sol.o.markdown +++ b/tests/testdata/outputs_expected/kinds_of_calls.sol.o.markdown @@ -24,7 +24,7 @@ The return value of an external call is not checked. Note that execution continu The return value of an external call is not checked. Note that execution continue even if the called contract throws. -## Message call to external contract +## External call to user-supplied address - SWC ID: 107 - Type: Warning - Contract: Unknown @@ -34,7 +34,7 @@ The return value of an external call is not checked. Note that execution continu ### Description -This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state. +The contract executes a function call with high gas to a user-supplied address. Note that the callee can contain arbitrary code and may re-enter any function in this contract. Review the business logic carefully to prevent unanticipated effects on the contract state. ## Unchecked CALL return value - SWC ID: 104 diff --git a/tests/testdata/outputs_expected/kinds_of_calls.sol.o.text b/tests/testdata/outputs_expected/kinds_of_calls.sol.o.text index 9a70b216..1f244935 100644 --- a/tests/testdata/outputs_expected/kinds_of_calls.sol.o.text +++ b/tests/testdata/outputs_expected/kinds_of_calls.sol.o.text @@ -18,14 +18,14 @@ Estimated Gas Usage: 1167 - 35919 The return value of an external call is not checked. Note that execution continue even if the called contract throws. -------------------- -==== Message call to external contract ==== +==== External call to user-supplied address ==== SWC ID: 107 Type: Warning Contract: Unknown Function name: _function_0xeea4c864 PC address: 1038 Estimated Gas Usage: 477 - 1229 -This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state. +The contract executes a function call with high gas to a user-supplied address. Note that the callee can contain arbitrary code and may re-enter any function in this contract. Review the business logic carefully to prevent unanticipated effects on the contract state. -------------------- ==== Unchecked CALL return value ==== diff --git a/tests/testdata/outputs_expected/returnvalue.sol.o.json b/tests/testdata/outputs_expected/returnvalue.sol.o.json index 4d41921e..cdcb199b 100644 --- a/tests/testdata/outputs_expected/returnvalue.sol.o.json +++ b/tests/testdata/outputs_expected/returnvalue.sol.o.json @@ -1,18 +1 @@ -{ - "error": null, - "issues": [ - { - "address": 290, - "contract": "Unknown", - "debug": "", - "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", - "function": "callnotchecked()", - "max_gas_used": 35941, - "min_gas_used": 1330, - "swc-id": "104", - "title": "Unchecked CALL return value", - "type": "Informational" - } - ], - "success": true -} +{"error": null, "issues": [{"address": 196, "contract": "Unknown", "debug": "", "description": "The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable.", "function": "callchecked()", "max_gas_used": 1210, "min_gas_used": 599, "swc-id": "107", "title": "External call", "type": "Informational"}, {"address": 285, "contract": "Unknown", "debug": "", "description": "The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable.", "function": "callnotchecked()", "max_gas_used": 1232, "min_gas_used": 621, "swc-id": "107", "title": "External call", "type": "Informational"}, {"address": 290, "contract": "Unknown", "debug": "", "description": "The return value of an external call is not checked. Note that execution continue even if the called contract throws.", "function": "callnotchecked()", "max_gas_used": 35941, "min_gas_used": 1330, "swc-id": "104", "title": "Unchecked CALL return value", "type": "Informational"}], "success": true} \ No newline at end of file diff --git a/tests/testdata/outputs_expected/returnvalue.sol.o.markdown b/tests/testdata/outputs_expected/returnvalue.sol.o.markdown index 652c22e6..d733d759 100644 --- a/tests/testdata/outputs_expected/returnvalue.sol.o.markdown +++ b/tests/testdata/outputs_expected/returnvalue.sol.o.markdown @@ -1,5 +1,29 @@ # Analysis results for test-filename.sol +## External call +- SWC ID: 107 +- Type: Informational +- Contract: Unknown +- Function name: `callchecked()` +- PC address: 196 +- Estimated Gas Usage: 599 - 1210 + +### Description + +The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable. + +## External call +- SWC ID: 107 +- Type: Informational +- Contract: Unknown +- Function name: `callnotchecked()` +- PC address: 285 +- Estimated Gas Usage: 621 - 1232 + +### Description + +The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable. + ## Unchecked CALL return value - SWC ID: 104 - Type: Informational diff --git a/tests/testdata/outputs_expected/returnvalue.sol.o.text b/tests/testdata/outputs_expected/returnvalue.sol.o.text index 98898844..29c4b0d0 100644 --- a/tests/testdata/outputs_expected/returnvalue.sol.o.text +++ b/tests/testdata/outputs_expected/returnvalue.sol.o.text @@ -1,3 +1,23 @@ +==== External call ==== +SWC ID: 107 +Type: Informational +Contract: Unknown +Function name: callchecked() +PC address: 196 +Estimated Gas Usage: 599 - 1210 +The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable. +-------------------- + +==== External call ==== +SWC ID: 107 +Type: Informational +Contract: Unknown +Function name: callnotchecked() +PC address: 285 +Estimated Gas Usage: 621 - 1232 +The contract executes a function call to an external address. Verify that the code at this address is trusted and immutable. +-------------------- + ==== Unchecked CALL return value ==== SWC ID: 104 Type: Informational From d72906e604cce29a284af90601d2e238736eef03 Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Thu, 29 Nov 2018 16:10:14 +0700 Subject: [PATCH 37/41] Remove old delegatecall tests --- tests/analysis/__init__.py | 0 tests/analysis/test_delegatecall.py | 268 ---------------------------- 2 files changed, 268 deletions(-) delete mode 100644 tests/analysis/__init__.py delete mode 100644 tests/analysis/test_delegatecall.py diff --git a/tests/analysis/__init__.py b/tests/analysis/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/analysis/test_delegatecall.py b/tests/analysis/test_delegatecall.py deleted file mode 100644 index 872b28d9..00000000 --- a/tests/analysis/test_delegatecall.py +++ /dev/null @@ -1,268 +0,0 @@ -from mythril.analysis.modules.delegatecall import detector -from mythril.analysis.ops import Call, Variable, VarType -from mythril.analysis.symbolic import SymExecWrapper -from mythril.laser.ethereum.cfg import Node -from mythril.laser.ethereum.state.environment import Environment -from mythril.laser.ethereum.state.account import Account -from mythril.laser.ethereum.state.global_state import GlobalState -import pytest -from unittest.mock import MagicMock, patch -import pytest_mock -from mythril.disassembler.disassembly import Disassembly - - -def test_concrete_call(): - # arrange - address = "0x10" - active_account = Account(address) - active_account.code = Disassembly("00") - environment = Environment(active_account, None, None, None, None, None) - - state = GlobalState(None, environment, None) - state.mstate.memory = ["placeholder", "calldata_bling_0"] - - node = Node("example") - node.contract_name = "the contract name" - node.function_name = "the function name" - - to = Variable(1, VarType.CONCRETE) - meminstart = Variable(1, VarType.CONCRETE) - call = Call(node, state, None, None, to, None) - - # act - issues = detector._concrete_call(call, state, address, meminstart) - - # assert - issue = issues[0] - assert issue.address == address - assert issue.contract == node.contract_name - assert issue.function == node.function_name - assert issue.title == "Call data forwarded with delegatecall()" - assert issue.type == "Informational" - assert ( - issue.description - == "This contract forwards its call data via DELEGATECALL in its fallback function." - " This means that any function in the called contract can be executed." - " Note that the callee contract will have access to the storage of the " - "calling contract.\nDELEGATECALL target: 0x1" - ) - - -def test_concrete_call_symbolic_to(): - # arrange - address = "0x10" - - active_account = Account(address) - active_account.code = Disassembly("00") - environment = Environment(active_account, None, None, None, None, None) - state = GlobalState(None, environment, None) - state.mstate.memory = ["placeholder", "calldata_bling_0"] - - node = Node("example") - node.contract_name = "the contract name" - node.function_name = "the function name" - - to = Variable("calldata_3", VarType.SYMBOLIC) - meminstart = Variable(1, VarType.CONCRETE) - call = Call(node, state, None, None, to, None) - - # act - issues = detector._concrete_call(call, state, address, meminstart) - - # assert - issue = issues[0] - assert issue.address == address - assert issue.contract == node.contract_name - assert issue.function == node.function_name - assert issue.title == "Call data forwarded with delegatecall()" - assert issue.type == "Informational" - assert issue.description == ( - "This contract forwards its call data via DELEGATECALL in its fallback function." - " This means that any function in the called contract can be executed." - " Note that the callee contract will have access to the storage of the " - "calling contract.\nDELEGATECALL target: calldata_3" - ) - - -def test_concrete_call_not_calldata(): - # arrange - state = GlobalState(None, None, None) - state.mstate.memory = ["placeholder", "not_calldata"] - meminstart = Variable(1, VarType.CONCRETE) - - # act - issues = detector._concrete_call(None, state, None, meminstart) - - # assert - assert issues == [] - - -def test_symbolic_call_storage_to(mocker): - # arrange - address = "0x10" - - active_account = Account(address) - active_account.code = Disassembly("00") - environment = Environment(active_account, None, None, None, None, None) - state = GlobalState(None, environment, None) - state.mstate.memory = ["placeholder", "calldata_bling_0"] - - node = Node("example") - node.contract_name = "the contract name" - node.function_name = "the function name" - - to = Variable("storage_1", VarType.SYMBOLIC) - call = Call(node, state, None, "Type: ", to, None) - - mocker.patch.object(SymExecWrapper, "__init__", lambda x, y: None) - statespace = SymExecWrapper(1) - - mocker.patch.object(statespace, "find_storage_write") - statespace.find_storage_write.return_value = "Function name" - - # act - issues = detector._symbolic_call(call, state, address, statespace) - - # assert - issue = issues[0] - assert issue.address == address - assert issue.contract == node.contract_name - assert issue.function == node.function_name - assert issue.title == "Type: to a user-supplied address" - assert issue.type == "Informational" - assert issue.description == ( - "This contract delegates execution to a contract address in storage slot 1." - " This storage slot can be written to by calling the function `Function name`." - " Be aware that the called contract gets unrestricted access to this contract's state." - ) - - -def test_symbolic_call_calldata_to(mocker): - # arrange - address = "0x10" - - active_account = Account(address) - active_account.code = Disassembly("00") - environment = Environment(active_account, None, None, None, None, None) - state = GlobalState(None, environment, None) - state.mstate.memory = ["placeholder", "calldata_bling_0"] - - node = Node("example") - node.contract_name = "the contract name" - node.function_name = "the function name" - - to = Variable("calldata", VarType.SYMBOLIC) - call = Call(node, state, None, "Type: ", to, None) - - mocker.patch.object(SymExecWrapper, "__init__", lambda x, y: None) - statespace = SymExecWrapper(1) - - mocker.patch.object(statespace, "find_storage_write") - statespace.find_storage_write.return_value = "Function name" - - # act - issues = detector._symbolic_call(call, state, address, statespace) - - # assert - issue = issues[0] - assert issue.address == address - assert issue.contract == node.contract_name - assert issue.function == node.function_name - assert issue.title == "Type: to a user-supplied address" - assert issue.type == "Informational" - assert issue.description == ( - "This contract delegates execution to a contract address obtained from calldata." - " Be aware that the called contract gets unrestricted access to this contract's state." - ) - - -@patch("mythril.laser.ethereum.state.global_state.GlobalState.get_current_instruction") -@patch("mythril.analysis.modules.delegatecall.detector._concrete_call") -@patch("mythril.analysis.modules.delegatecall.detector._symbolic_call") -def test_delegate_call(sym_mock, concrete_mock, curr_instruction): - # arrange - # sym_mock = mocker.patch.object(delegatecall, "_symbolic_call") - # concrete_mock = mocker.patch.object(delegatecall, "_concrete_call") - sym_mock.return_value = [] - concrete_mock.return_value = [] - curr_instruction.return_value = {"address": "0x10"} - - active_account = Account("0x10") - active_account.code = Disassembly("00") - - environment = Environment(active_account, None, None, None, None, None) - state = GlobalState(None, environment, Node) - state.mstate.memory = ["placeholder", "calldata_bling_0"] - state.mstate.stack = [1, 2, 3] - assert state.get_current_instruction() == {"address": "0x10"} - - node = Node("example") - node.contract_name = "the contract name" - node.function_name = "fallback" - - to = Variable("storage_1", VarType.SYMBOLIC) - call = Call(node, state, None, "DELEGATECALL", to, None) - - statespace = MagicMock() - statespace.calls = [call] - - # act - detector.execute(statespace) - - # assert - assert concrete_mock.call_count == 1 - assert sym_mock.call_count == 1 - - -@patch("mythril.analysis.modules.delegatecall.detector._concrete_call") -@patch("mythril.analysis.modules.delegatecall.detector._symbolic_call") -def test_delegate_call_not_delegate(sym_mock, concrete_mock): - # arrange - # sym_mock = mocker.patch.object(delegatecall, "_symbolic_call") - # concrete_mock = mocker.patch.object(delegatecall, "_concrete_call") - sym_mock.return_value = [] - concrete_mock.return_value = [] - - node = Node("example") - node.function_name = "fallback" - - to = Variable("storage_1", VarType.SYMBOLIC) - call = Call(node, None, None, "NOT_DELEGATECALL", to, None) - - statespace = MagicMock() - statespace.calls = [call] - - # act - issues = detector.execute(statespace) - - # assert - assert issues == [] - assert concrete_mock.call_count == 0 - assert sym_mock.call_count == 0 - - -@patch("mythril.analysis.modules.delegatecall.detector._concrete_call") -@patch("mythril.analysis.modules.delegatecall.detector._symbolic_call") -def test_delegate_call_not_fallback(sym_mock, concrete_mock): - # arrange - # sym_mock = mocker.patch.object(delegatecall, "_symbolic_call") - # concrete_mock = mocker.patch.object(delegatecall, "_concrete_call") - sym_mock.return_value = [] - concrete_mock.return_value = [] - - node = Node("example") - node.function_name = "not_fallback" - - to = Variable("storage_1", VarType.SYMBOLIC) - call = Call(node, None, None, "DELEGATECALL", to, None) - - statespace = MagicMock() - statespace.calls = [call] - - # act - issues = detector.execute(statespace) - - # assert - assert issues == [] - assert concrete_mock.call_count == 0 - assert sym_mock.call_count == 0 From 2aa144a7a88d1c2e93aa82fb236fa2aca58a4378 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Thu, 29 Nov 2018 17:14:32 +0530 Subject: [PATCH 38/41] Remove the trailing "," in signaturesDB (#766) That corresponding statement in worst case is `if (False or False or False,)` simplifies to `if (False,):` which simplifies to `True` , so the code below is unreachable --- mythril/support/signatures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mythril/support/signatures.py b/mythril/support/signatures.py index 210f6f90..1f517623 100644 --- a/mythril/support/signatures.py +++ b/mythril/support/signatures.py @@ -129,7 +129,7 @@ class SignatureDB(object): if ( not self.enable_online_lookup or byte_sig in self.online_lookup_miss - or time.time() < self.online_lookup_timeout, + or time.time() < self.online_lookup_timeout ): return [] From 97acd0c0bf84e48500bd4f15002452356590d39f Mon Sep 17 00:00:00 2001 From: Bernhard Mueller Date: Thu, 29 Nov 2018 20:42:13 +0700 Subject: [PATCH 39/41] Update version.py --- mythril/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mythril/version.py b/mythril/version.py index 3c0b8300..bbc53292 100644 --- a/mythril/version.py +++ b/mythril/version.py @@ -1,3 +1,3 @@ # This file is suitable for sourcing inside POSIX shell, e.g. bash as # well as for importing into Python -VERSION = "v0.19.7" # NOQA +VERSION = "v0.19.8" # NOQA From 3774c4f8eaf6103a4d33dc8b4360f3352a3ac739 Mon Sep 17 00:00:00 2001 From: Nathan Date: Fri, 30 Nov 2018 06:08:08 -0500 Subject: [PATCH 40/41] create function for returndatacopy and pop arguments (#774) --- mythril/laser/ethereum/instructions.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/mythril/laser/ethereum/instructions.py b/mythril/laser/ethereum/instructions.py index 06b907ef..64e27dcd 100644 --- a/mythril/laser/ethereum/instructions.py +++ b/mythril/laser/ethereum/instructions.py @@ -131,7 +131,7 @@ class Instruction: def evaluate(self, global_state: GlobalState, post=False) -> List[GlobalState]: """ Performs the mutation for this instruction """ # Generalize some ops - # logging.debug("Evaluating {}".format(self.op_code)) + logging.debug("Evaluating {}".format(self.op_code)) op = self.op_code.lower() if self.op_code.startswith("PUSH"): op = "push" @@ -773,6 +773,14 @@ class Instruction: return [global_state] + @StateTransition() + def returndatacopy_(self, global_state: GlobalState) -> List[GlobalState]: + # FIXME: not implemented + state = global_state.mstate + start, s2, size = state.stack.pop(), state.stack.pop(), state.stack.pop() + + return [global_state] + @StateTransition() def returndatasize_(self, global_state: GlobalState) -> List[GlobalState]: global_state.mstate.stack.append(global_state.new_bitvec("returndatasize", 256)) From ee90356eee5aeab74dd9af049b23ca99cfe82dcf Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Fri, 30 Nov 2018 17:47:55 +0530 Subject: [PATCH 41/41] Fixes the calldata problem --- mythril/laser/ethereum/state/calldata.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mythril/laser/ethereum/state/calldata.py b/mythril/laser/ethereum/state/calldata.py index 3cbbf834..5e53bab5 100644 --- a/mythril/laser/ethereum/state/calldata.py +++ b/mythril/laser/ethereum/state/calldata.py @@ -108,18 +108,17 @@ class SymbolicCalldata(BaseCalldata): :param tx_id: Id of the transaction that the calldata is for. """ self._reads = [] - self._size = BitVec("calldatasize", 256) + self._size = BitVec(str(tx_id) + "_calldatasize", 256) super().__init__(tx_id) def _load(self, item: Union[int, ExprRef], clean=False) -> Any: x = BitVecVal(item, 256) if isinstance(item, int) else item symbolic_base_value = If( - x > self._size, + x >= self._size, BitVecVal(0, 8), BitVec("{}_calldata_{}".format(self.tx_id, str(item)), 8), ) - return_value = symbolic_base_value for r_index, r_value in self._reads: return_value = If(r_index == item, r_value, return_value)