Merge remote-tracking branch 'upstream/master' into features/codecopy

pull/379/head
Joran Honig 6 years ago
commit ff2261bb90
  1. 35
      .github/ISSUE_TEMPLATE/analysis-module.md
  2. 68
      .github/ISSUE_TEMPLATE/bug-report.md
  3. 28
      .github/ISSUE_TEMPLATE/bug_report.md
  4. 24
      .github/ISSUE_TEMPLATE/feature-request.md
  5. 2
      mythril/analysis/modules/exceptions.py
  6. 1
      mythril/analysis/modules/integer.py
  7. 2
      mythril/analysis/solver.py
  8. 12
      mythril/ether/soliditycontract.py
  9. 16
      mythril/laser/ethereum/instructions.py
  10. 13
      mythril/laser/ethereum/state.py
  11. 2
      mythril/laser/ethereum/svm.py
  12. 2
      mythril/laser/ethereum/taint_analysis.py
  13. 6
      mythril/mythril.py
  14. 4
      mythril/support/signatures.py
  15. 6
      mythril/support/truffle.py
  16. 10
      tests/taint_runner_test.py
  17. 19
      tests/testdata/input_contracts/environments.sol
  18. 1
      tests/testdata/inputs/environments.sol.o
  19. 259
      tests/testdata/outputs_expected/environments.sol.o.easm
  20. 56
      tests/testdata/outputs_expected/environments.sol.o.graph.html
  21. 1
      tests/testdata/outputs_expected/environments.sol.o.json
  22. 37
      tests/testdata/outputs_expected/environments.sol.o.markdown
  23. 27
      tests/testdata/outputs_expected/environments.sol.o.text
  24. 54936
      tests/testdata/outputs_expected_laser_result/environments.sol.json

@ -4,13 +4,34 @@ about: Create an analysis module feature request
---
# Detection issue:
Name the issue that should be detected using the analysis module
Please remove any of the optional sections if they are not applicable.
## Description:
Provide a detailed description of the vulnerabiltity that should be detected
## Description
## Link
Provide resources that can help with implementing the analysis module
Replace this text with a description of an vulnerability that should be
detected by a Mythril analysis module.
## Implementation details:Initial implementation ideas/instruction
## Tests
_This section is optional._
Replace this text with suggestions on how to test the feature,
if it is not obvious. This might require certain Solidity source,
bytecode, or a Truffle project. You can also provide
links to existing code.
## Implementation details
_This section is optional._
If you have thoughts about how to implement the analysis, feel free
replace this text with that.
## Links
_This section is optional._
Replace this text with any links describing the issue or pointing to resources
that can help in implementing the analysis
Thanks for helping!

@ -0,0 +1,68 @@
---
name: Bug report
about: Tell us about Mythril bugs to help us improve
---
_Note: did you notice that there is now a template for requesting new features?_
Please remove any of the optional sections if they are not applicable.
## Description
Replace this text with a clear and concise description of the bug.
## How to Reproduce
Please show both the input you gave and the
output you got in describing how to reproduce the bug:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
or give a complete console log with input and output
```console
$ myth <command-line-options>
==== Exception state ====
Type: ...
Contract: ...
Function name: ...
...
$
```
If there is a Solidity source code, a truffle project, or bytecode
that is involved, please provide that or links to it.
## Expected behavior
A clear and concise description of what you expected to happen.
## Screenshots
_This section is optional._
If applicable, add screenshots to help explain your problem.
## Environment
_This section sometimes is optional but helpful to us._
Please modify for your setup
- Mythril version: output from `myth --version` or `pip show mythril`
- Solidity compiler and version: `solc --version`
- Python version: `python -V`
- OS: [e.g. iOS]
- OS Version [e.g. 22]
## Additional Environment or Context
_This section is optional._
Add any other context about the problem here or special environment setup
Thanks for helping!

@ -1,28 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

@ -0,0 +1,24 @@
---
name: Feature Request
about: Tell us about a new feature that would make Mythril better
---
## Description
Replace this text with a short description of the feature.
## Background
Replace this text with any additional background for the
feature, for example: user scenarios, or the value of the feature.
## Tests
_This section is optional._
Replace this text with suggestions on how to test the feature,
if it is not obvious. This might require certain Solidity source,
bytecode, or a Truffle project. You can also provide
links to existing code.
Thanks for helping!

@ -24,9 +24,7 @@ def execute(statespace):
for state in node.states:
instruction = state.get_current_instruction()
if(instruction['opcode'] == "ASSERT_FAIL"):
try:
model = solver.get_model(node.constraints)
address = state.get_current_instruction()['address']

@ -108,7 +108,6 @@ def _verify_integer_overflow(statespace, node, expr, state, model, constraint, o
return _try_constraints(node.constraints, [Not(constraint)]) is not None
def _try_constraints(constraints, new_constraints):
"""
Tries new constraints

@ -4,7 +4,7 @@ import logging
def get_model(constraints):
s = Solver()
s.set("timeout", 10000)
s.set("timeout", 100000)
for constraint in constraints:
s.add(constraint)

@ -1,8 +1,9 @@
import mythril.laser.ethereum.util as helper
from mythril.ether.ethcontract import ETHContract
from mythril.ether.util import *
from mythril.ether.util import get_solc_json
from mythril.exceptions import NoContractFoundError
class SourceMapping:
def __init__(self, solidity_file_idx, offset, length, lineno):
@ -53,7 +54,7 @@ class SolidityContract(ETHContract):
# If a contract name has been specified, find the bytecode of that specific contract
if name:
for key, contract in data['contracts'].items():
for key, contract in sorted(data['contracts'].items()):
filename, _name = key.split(":")
if filename == input_file and name == _name and len(contract['bin-runtime']):
@ -66,7 +67,7 @@ class SolidityContract(ETHContract):
# If no contract name is specified, get the last bytecode entry for the input file
else:
for key, contract in data['contracts'].items():
for key, contract in sorted(data['contracts'].items()):
filename, name = key.split(":")
if filename == input_file and len(contract['bin-runtime']):
@ -91,8 +92,7 @@ class SolidityContract(ETHContract):
if len(mapping) > 2 and len(mapping[2]) > 0:
idx = int(mapping[2])
lineno = self.solidity_files[idx].data[0:offset].count('\n') + 1
lineno = self.solidity_files[idx].data.encode('utf-8')[0:offset].count('\n'.encode('utf-8')) + 1
self.mappings.append(SourceMapping(idx, offset, length, lineno))
@ -109,7 +109,7 @@ class SolidityContract(ETHContract):
offset = self.mappings[index].offset
length = self.mappings[index].length
code = solidity_file.data[offset:offset + length]
code = solidity_file.data.encode('utf-8')[offset:offset + length].decode('utf-8')
lineno = self.mappings[index].lineno
return SourceCodeInfo(filename, lineno, code)

@ -742,13 +742,8 @@ class Instruction:
index = str(index)
try:
# Create a fresh copy of the account object before modifying storage
for k in global_state.accounts:
if global_state.accounts[k] == global_state.environment.active_account:
global_state.accounts[k] = deepcopy(global_state.accounts[k])
global_state.environment.active_account = global_state.accounts[k]
break
global_state.environment.active_account = deepcopy(global_state.environment.active_account)
global_state.accounts[global_state.environment.active_account.address] = global_state.environment.active_account
global_state.environment.active_account.storage[index] = value
except KeyError:
@ -813,7 +808,7 @@ class Instruction:
new_state = copy(global_state)
new_state.mstate.pc = index
new_state.mstate.depth += 1
new_state.mstate.constraints.append(condi)
new_state.mstate.constraints.append(simplify(condi))
states.append(new_state)
else:
@ -821,12 +816,11 @@ class Instruction:
# False case
negated = Not(condition) if type(condition) == BoolRef else condition == 0
sat = not is_false(simplify(negated)) if type(condi) == BoolRef else not negated
if sat:
if (type(negated) == bool and negated) or (type(negated) == BoolRef and not is_false(simplify(negated))):
new_state = copy(global_state)
new_state.mstate.depth += 1
new_state.mstate.constraints.append(negated)
new_state.mstate.constraints.append(simplify(negated))
states.append(new_state)
else:
logging.debug("Pruned unreachable states.")

@ -38,8 +38,14 @@ class Account:
def add_balance(self, balance):
self.balance += balance
def get_storage(self, index):
return self.storage[index] if index in self.storage.keys() else BitVec("storage_" + str(index), 256)
# def get_storage(self, index):
# return BitVec("storage_" + str(index), 256)
# if index in self.storage.keys():
# return self.storage[index]
# else:
# symbol = BitVec("storage_" + str(index), 256)
# self.storage[index] = symbol
# return symbol
@property
def as_dict(self):
@ -80,6 +86,7 @@ class Environment:
def __str__(self):
return str(self.as_dict)
@property
def as_dict(self):
return dict(active_account=self.active_account, sender=self.sender, calldata=self.calldata,
@ -147,7 +154,7 @@ class GlobalState:
def __copy__(self):
accounts = self.accounts
accounts = copy(self.accounts)
environment = copy(self.environment)
mstate = deepcopy(self.mstate)
call_stack = copy(self.call_stack)

@ -70,7 +70,7 @@ class LaserEVM:
try:
new_states, op_code = self.execute_state(global_state)
except NotImplementedError:
logging.debug("Encountered unimplemented instruction")
logging.info("Encountered unimplemented instruction: {}".format(op_code))
continue
if len(new_states) == 0:

@ -118,7 +118,7 @@ class TaintRunner:
direct_children = [statespace.nodes[edge.node_to] for edge in statespace.edges if edge.node_from == node.uid]
children = []
for child in direct_children:
if child.states[0].environment == environment:
if child.states[0].environment.active_account.address == environment.active_account.address:
children.append(child)
else:
children += TaintRunner.children(child, statespace, environment)

@ -332,6 +332,9 @@ class Mythril(object):
# import signatures from solidity source
with open(file, encoding="utf-8") as f:
self.sigs.import_from_solidity_source(f.read())
# Save updated function signatures
self.sigs.write() # dump signatures to disk (previously opened file or default location)
if contract_name is not None:
contract = SolidityContract(file, contract_name, solc_args=self.solc_args)
self.contracts.append(contract)
@ -341,6 +344,7 @@ class Mythril(object):
self.contracts.append(contract)
contracts.append(contract)
except FileNotFoundError:
raise CriticalError("Input file not found: " + file)
except CompilerError as e:
@ -348,8 +352,6 @@ class Mythril(object):
except NoContractFoundError:
logging.info("The file " + file + " does not contain a compilable contract.")
# Save updated function signatures
self.sigs.write() # dump signatures to disk (previously opened file or default location)
return address, contracts

@ -154,7 +154,6 @@ class SignatureDb(object):
"""
if not sighash.startswith("0x"):
sighash = "0x%s" % sighash # normalize sighash format
if self.enable_online_lookup and not self.signatures.get(sighash) and sighash not in self.online_lookup_miss and time.time() > self.online_directory_unavailable_until:
# online lookup enabled, and signature not in cache, sighash was not a miss earlier, and online directory not down
logging.debug("Signatures: performing online lookup for sighash %r" % sighash)
@ -169,8 +168,11 @@ class SignatureDb(object):
except FourByteDirectoryOnlineLookupError as fbdole:
self.online_directory_unavailable_until = time.time() + 2 * 60 # wait at least 2 mins to try again
logging.warning("online function signature lookup not available. will not try to lookup hash for the next 2 minutes. exception: %r" % fbdole)
if type(self.signatures[sighash]) != list:
return [self.signatures[sighash]]
return self.signatures[sighash] # raise keyerror
def __getitem__(self, item):
"""
Provide dict interface Signatures()[sighash]

@ -1,4 +1,5 @@
import os
from pathlib import PurePath
import re
import sys
import json
@ -31,6 +32,7 @@ def analyze_truffle_project(args):
try:
name = contractdata['contractName']
bytecode = contractdata['deployedBytecode']
filename = PurePath(contractdata['sourcePath']).name
except:
print("Unable to parse contract data. Please use Truffle 4 to compile your project.")
sys.exit()
@ -74,7 +76,7 @@ def analyze_truffle_project(args):
if len(mapping) > 2 and len(mapping[2]) > 0:
idx = int(mapping[2])
lineno = source[0:offset].count('\n') + 1
lineno = source.encode('utf-8')[0:offset].count('\n'.encode('utf-8')) + 1
mappings.append(SourceMapping(idx, offset, length, lineno))
@ -88,7 +90,7 @@ def analyze_truffle_project(args):
length = mappings[index].length
issue.filename = filename
issue.code = source[offset:offset + length]
issue.code = source.encode('utf-8')[offset:offset + length].decode('utf-8')
issue.lineno = mappings[index].lineno
except IndexError:
logging.debug("No code mapping at index %d", index)

@ -3,7 +3,7 @@ import pytest
from pytest_mock import mocker
from mythril.laser.ethereum.taint_analysis import *
from mythril.laser.ethereum.svm import GlobalState, Node, Edge, LaserEVM
from mythril.laser.ethereum.state import MachineState
from mythril.laser.ethereum.state import MachineState, Account, Environment
def test_execute_state(mocker):
@ -57,12 +57,14 @@ def test_execute_node(mocker):
def test_execute(mocker):
state_1 = GlobalState(None, None, None, MachineState(gas=10000000))
active_account = Account('0x00')
environment = Environment(active_account, None, None, None, None, None)
state_1 = GlobalState(None, environment, None, MachineState(gas=10000000))
state_1.mstate.stack = [1, 2]
mocker.patch.object(state_1, 'get_current_instruction')
state_1.get_current_instruction.return_value = {"opcode": "PUSH"}
state_2 = GlobalState(None, None, None, MachineState(gas=10000000))
state_2 = GlobalState(None, environment, None, MachineState(gas=10000000))
state_2.mstate.stack = [1, 2, 3]
mocker.patch.object(state_2, 'get_current_instruction')
state_2.get_current_instruction.return_value = {"opcode": "ADD"}
@ -70,7 +72,7 @@ def test_execute(mocker):
node_1 = Node("Test contract")
node_1.states = [state_1, state_2]
state_3 = GlobalState(None, None, None, MachineState(gas=10000000))
state_3 = GlobalState(None, environment, None, MachineState(gas=10000000))
state_3.mstate.stack = [1, 2]
mocker.patch.object(state_3, 'get_current_instruction')
state_3.get_current_instruction.return_value = {"opcode": "ADD"}

@ -0,0 +1,19 @@
pragma solidity ^0.4.16;
contract IntegerOverflow2 {
uint256 public count = 7;
mapping(address => uint256) balances;
function batchTransfer(address[] _receivers, uint256 _value) public returns(bool){
uint cnt = _receivers.length;
uint256 amount = uint256(cnt) * _value;
require(cnt > 0 && cnt <= 20);
balances[msg.sender] -=amount;
return true;
}
}

@ -0,0 +1 @@
60806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306661abd1461005157806383f12fec1461007c575b600080fd5b34801561005d57600080fd5b50610066610104565b6040518082815260200191505060405180910390f35b34801561008857600080fd5b506100ea600480360381019080803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091929192908035906020019092919050505061010a565b604051808215151515815260200191505060405180910390f35b60005481565b6000806000845191508382029050600082118015610129575060148211155b151561013457600080fd5b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550600192505050929150505600a165627a7a7230582016b81221eb990028632ba9b34d3c01599d24acdb5b81dd6789845696f5db257c0029

@ -0,0 +1,259 @@
0 PUSH1 0x80
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 0x06661abd
60 EQ
61 PUSH2 0x0051
64 JUMPI
65 DUP1
66 PUSH4 0x83f12fec
71 EQ
72 PUSH2 0x007c
75 JUMPI
76 JUMPDEST
77 PUSH1 0x00
79 DUP1
80 REVERT
81 JUMPDEST
82 CALLVALUE
83 DUP1
84 ISZERO
85 PUSH2 0x005d
88 JUMPI
89 PUSH1 0x00
91 DUP1
92 REVERT
93 JUMPDEST
94 POP
95 PUSH2 0x0066
98 PUSH2 0x0104
101 JUMP
102 JUMPDEST
103 PUSH1 0x40
105 MLOAD
106 DUP1
107 DUP3
108 DUP2
109 MSTORE
110 PUSH1 0x20
112 ADD
113 SWAP2
114 POP
115 POP
116 PUSH1 0x40
118 MLOAD
119 DUP1
120 SWAP2
121 SUB
122 SWAP1
123 RETURN
124 JUMPDEST
125 CALLVALUE
126 DUP1
127 ISZERO
128 PUSH2 0x0088
131 JUMPI
132 PUSH1 0x00
134 DUP1
135 REVERT
136 JUMPDEST
137 POP
138 PUSH2 0x00ea
141 PUSH1 0x04
143 DUP1
144 CALLDATASIZE
145 SUB
146 DUP2
147 ADD
148 SWAP1
149 DUP1
150 DUP1
151 CALLDATALOAD
152 SWAP1
153 PUSH1 0x20
155 ADD
156 SWAP1
157 DUP3
158 ADD
159 DUP1
160 CALLDATALOAD
161 SWAP1
162 PUSH1 0x20
164 ADD
165 SWAP1
166 DUP1
167 DUP1
168 PUSH1 0x20
170 MUL
171 PUSH1 0x20
173 ADD
174 PUSH1 0x40
176 MLOAD
177 SWAP1
178 DUP2
179 ADD
180 PUSH1 0x40
182 MSTORE
183 DUP1
184 SWAP4
185 SWAP3
186 SWAP2
187 SWAP1
188 DUP2
189 DUP2
190 MSTORE
191 PUSH1 0x20
193 ADD
194 DUP4
195 DUP4
196 PUSH1 0x20
198 MUL
199 DUP1
200 DUP3
201 DUP5
202 CALLDATACOPY
203 DUP3
204 ADD
205 SWAP2
206 POP
207 POP
208 POP
209 POP
210 POP
211 POP
212 SWAP2
213 SWAP3
214 SWAP2
215 SWAP3
216 SWAP1
217 DUP1
218 CALLDATALOAD
219 SWAP1
220 PUSH1 0x20
222 ADD
223 SWAP1
224 SWAP3
225 SWAP2
226 SWAP1
227 POP
228 POP
229 POP
230 PUSH2 0x010a
233 JUMP
234 JUMPDEST
235 PUSH1 0x40
237 MLOAD
238 DUP1
239 DUP3
240 ISZERO
241 ISZERO
242 ISZERO
243 ISZERO
244 DUP2
245 MSTORE
246 PUSH1 0x20
248 ADD
249 SWAP2
250 POP
251 POP
252 PUSH1 0x40
254 MLOAD
255 DUP1
256 SWAP2
257 SUB
258 SWAP1
259 RETURN
260 JUMPDEST
261 PUSH1 0x00
263 SLOAD
264 DUP2
265 JUMP
266 JUMPDEST
267 PUSH1 0x00
269 DUP1
270 PUSH1 0x00
272 DUP5
273 MLOAD
274 SWAP2
275 POP
276 DUP4
277 DUP3
278 MUL
279 SWAP1
280 POP
281 PUSH1 0x00
283 DUP3
284 GT
285 DUP1
286 ISZERO
287 PUSH2 0x0129
290 JUMPI
291 POP
292 PUSH1 0x14
294 DUP3
295 GT
296 ISZERO
297 JUMPDEST
298 ISZERO
299 ISZERO
300 PUSH2 0x0134
303 JUMPI
304 PUSH1 0x00
306 DUP1
307 REVERT
308 JUMPDEST
309 DUP1
310 PUSH1 0x01
312 PUSH1 0x00
314 CALLER
315 PUSH20 0xffffffffffffffffffffffffffffffffffffffff
336 AND
337 PUSH20 0xffffffffffffffffffffffffffffffffffffffff
358 AND
359 DUP2
360 MSTORE
361 PUSH1 0x20
363 ADD
364 SWAP1
365 DUP2
366 MSTORE
367 PUSH1 0x20
369 ADD
370 PUSH1 0x00
372 SHA3
373 PUSH1 0x00
375 DUP3
376 DUP3
377 SLOAD
378 SUB
379 SWAP3
380 POP
381 POP
382 DUP2
383 SWAP1
384 SSTORE
385 POP
386 PUSH1 0x01
388 SWAP3
389 POP
390 POP
391 POP
392 SWAP3
393 SWAP2
394 POP
395 POP
396 JUMP
397 STOP

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
{"error": null, "issues": [{"address": 158, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "A possible integer overflow exists in the function `_function_0x83f12fec`.\nThe addition or multiplication may result in a value higher than the maximum representable integer.", "function": "_function_0x83f12fec", "title": "Integer Overflow", "type": "Warning"}, {"address": 278, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "A possible integer overflow exists in the function `_function_0x83f12fec`.\nThe addition or multiplication may result in a value higher than the maximum representable integer.", "function": "_function_0x83f12fec", "title": "Integer Overflow", "type": "Warning"}, {"address": 378, "contract": "Unknown", "debug": "<DEBUG-DATA>", "description": "A possible integer underflow exists in the function `_function_0x83f12fec`.\nThe subtraction may result in a value < 0.", "function": "_function_0x83f12fec", "title": "Integer Underflow", "type": "Warning"}], "success": true}

@ -0,0 +1,37 @@
# Analysis results for test-filename.sol
## Integer Overflow
- Type: Warning
- Contract: Unknown
- Function name: `_function_0x83f12fec`
- PC address: 158
### Description
A possible integer overflow exists in the function `_function_0x83f12fec`.
The addition or multiplication may result in a value higher than the maximum representable integer.
## Integer Overflow
- Type: Warning
- Contract: Unknown
- Function name: `_function_0x83f12fec`
- PC address: 278
### Description
A possible integer overflow exists in the function `_function_0x83f12fec`.
The addition or multiplication may result in a value higher than the maximum representable integer.
## Integer Underflow
- Type: Warning
- Contract: Unknown
- Function name: `_function_0x83f12fec`
- PC address: 378
### Description
A possible integer underflow exists in the function `_function_0x83f12fec`.
The subtraction may result in a value < 0.

@ -0,0 +1,27 @@
==== Integer Overflow ====
Type: Warning
Contract: Unknown
Function name: _function_0x83f12fec
PC address: 158
A possible integer overflow exists in the function `_function_0x83f12fec`.
The addition or multiplication may result in a value higher than the maximum representable integer.
--------------------
==== Integer Overflow ====
Type: Warning
Contract: Unknown
Function name: _function_0x83f12fec
PC address: 278
A possible integer overflow exists in the function `_function_0x83f12fec`.
The addition or multiplication may result in a value higher than the maximum representable integer.
--------------------
==== Integer Underflow ====
Type: Warning
Contract: Unknown
Function name: _function_0x83f12fec
PC address: 378
A possible integer underflow exists in the function `_function_0x83f12fec`.
The subtraction may result in a value < 0.
--------------------

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save