Merge branch 'master' into dev

pull/68/head
Josselin 6 years ago
commit 44dfa2b41c
  1. 2
      README.md
  2. 2
      scripts/travis_test.sh
  3. 5
      slither/analyses/taint/common.py
  4. 4
      slither/analyses/write/are_variables_written.py
  5. 32
      slither/core/cfg/node.py
  6. 3
      slither/core/declarations/solidity_variables.py
  7. 6
      slither/core/variables/variable.py
  8. 2
      slither/detectors/attributes/old_solc.py
  9. 2
      slither/detectors/functions/arbitrary_send.py
  10. 2
      slither/detectors/functions/external_function.py
  11. 9
      slither/detectors/variables/uninitialized_state_variables.py
  12. 33
      slither/slithir/convert.py
  13. 2
      slither/slithir/operations/__init__.py
  14. 2
      slither/slithir/operations/assignment.py
  15. 26
      slither/slithir/operations/balance.py
  16. 3
      slither/slithir/operations/binary.py
  17. 10
      slither/slithir/operations/event_call.py
  18. 11
      slither/slithir/operations/high_level_call.py
  19. 11
      slither/slithir/operations/init_array.py
  20. 11
      slither/slithir/operations/internal_call.py
  21. 12
      slither/slithir/operations/internal_dynamic_call.py
  22. 26
      slither/slithir/operations/length.py
  23. 11
      slither/slithir/operations/new_array.py
  24. 12
      slither/slithir/operations/new_contract.py
  25. 11
      slither/slithir/operations/new_structure.py
  26. 5
      slither/slithir/variables/constant.py
  27. 1
      slither/slithir/variables/tuple.py
  28. 10
      slither/solc_parsing/expressions/expression_parsing.py

@ -60,7 +60,7 @@ Num | Detector | What it Detects | Impact | Confidence
11 | `low-level-calls` | Low level calls | Informational | High 11 | `low-level-calls` | Low level calls | Informational | High
12 | `naming-convention` | Conformance to Solidity naming conventions | Informational | High 12 | `naming-convention` | Conformance to Solidity naming conventions | Informational | High
13 | `pragma` | If different pragma directives are used | Informational | High 13 | `pragma` | If different pragma directives are used | Informational | High
14 | `solc-version` | If an old version of Solidity used (<0.4.23) | Informational | High 14 | `solc-version` | Old versions of Solidity (< 0.4.23) | Informational | High
15 | `unused-state` | Unused state variables | Informational | High 15 | `unused-state` | Unused state variables | Informational | High
[Contact us](https://www.trailofbits.com/contact/) to get access to additional detectors. [Contact us](https://www.trailofbits.com/contact/) to get access to additional detectors.

@ -30,7 +30,7 @@ test_slither tests/complex_func.sol "complex-function" 3
test_slither tests/inline_assembly_contract.sol "assembly" 1 test_slither tests/inline_assembly_contract.sol "assembly" 1
test_slither tests/inline_assembly_library.sol "assembly" 2 test_slither tests/inline_assembly_library.sol "assembly" 2
test_slither tests/low_level_calls.sol "low-level-calls" 1 test_slither tests/low_level_calls.sol "low-level-calls" 1
test_slither tests/const_state_variables.sol "const-candidates-state" 2 test_slither tests/const_state_variables.sol "constable-states" 2
test_slither tests/external_function.sol "external-function" 4 test_slither tests/external_function.sol "external-function" 4
test_slither tests/naming_convention.sol "naming-convention" 12 test_slither tests/naming_convention.sol "naming-convention" 12

@ -1,4 +1,4 @@
from slither.slithir.operations import (Index, Member) from slither.slithir.operations import (Index, Member, Length, Balance)
def iterate_over_irs(irs, transfer_func, taints): def iterate_over_irs(irs, transfer_func, taints):
refs = {} refs = {}
@ -6,6 +6,9 @@ def iterate_over_irs(irs, transfer_func, taints):
if isinstance(ir, (Index, Member)): if isinstance(ir, (Index, Member)):
refs[ir.lvalue] = ir.variable_left refs[ir.lvalue] = ir.variable_left
if isinstance(ir, (Length, Balance)):
refs[ir.lvalue] = ir.value
if isinstance(ir, Index): if isinstance(ir, Index):
read = [ir.variable_left] read = [ir.variable_left]
else: else:

@ -4,7 +4,7 @@
from slither.core.cfg.node import NodeType from slither.core.cfg.node import NodeType
from slither.core.declarations import SolidityFunction from slither.core.declarations import SolidityFunction
from slither.slithir.operations import (Index, Member, OperationWithLValue, from slither.slithir.operations import (Index, Member, OperationWithLValue,
SolidityCall) SolidityCall, Length, Balance)
from slither.slithir.variables import ReferenceVariable from slither.slithir.variables import ReferenceVariable
@ -27,6 +27,8 @@ def _visit(node, visited, variables_written, variables_to_write):
continue continue
if isinstance(ir, (Index, Member)): if isinstance(ir, (Index, Member)):
refs[ir.lvalue] = ir.variable_left refs[ir.lvalue] = ir.variable_left
if isinstance(ir, (Length, Balance)):
refs[ir.lvalue] = ir.value
variables_written = variables_written + [ir.lvalue] variables_written = variables_written + [ir.lvalue]
lvalue = ir.lvalue lvalue = ir.lvalue

@ -3,27 +3,24 @@
""" """
import logging import logging
from slither.core.children.child_function import ChildFunction
from slither.core.declarations import Contract
from slither.core.declarations.solidity_variables import (SolidityFunction,
SolidityVariable)
from slither.core.source_mapping.source_mapping import SourceMapping from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.variables.variable import Variable
from slither.core.variables.state_variable import StateVariable from slither.core.variables.state_variable import StateVariable
from slither.core.variables.variable import Variable
from slither.slithir.convert import convert_expression
from slither.slithir.operations import (Balance, HighLevelCall, Index,
InternalCall, Length, LibraryCall,
LowLevelCall, Member,
OperationWithLValue, SolidityCall)
from slither.slithir.variables import (Constant, ReferenceVariable,
TemporaryVariable, TupleVariable)
from slither.visitors.expression.expression_printer import ExpressionPrinter from slither.visitors.expression.expression_printer import ExpressionPrinter
from slither.visitors.expression.read_var import ReadVar from slither.visitors.expression.read_var import ReadVar
from slither.visitors.expression.write_var import WriteVar from slither.visitors.expression.write_var import WriteVar
from slither.core.children.child_function import ChildFunction
from slither.core.declarations.solidity_variables import SolidityVariable, SolidityFunction
from slither.slithir.convert import convert_expression
from slither.slithir.operations import OperationWithLValue, Index, Member, LowLevelCall, SolidityCall, HighLevelCall, InternalCall, LibraryCall
from slither.slithir.variables import Constant, ReferenceVariable, TemporaryVariable, TupleVariable
from slither.core.declarations import Contract
logger = logging.getLogger("Node") logger = logging.getLogger("Node")
class NodeType: class NodeType:
@ -370,11 +367,10 @@ class Node(SourceMapping, ChildFunction):
def is_slithir_var(var): def is_slithir_var(var):
return isinstance(var, (Constant, ReferenceVariable, TemporaryVariable, TupleVariable)) return isinstance(var, (Constant, ReferenceVariable, TemporaryVariable, TupleVariable))
for ir in self.irs: for ir in self.irs:
self._vars_read += [v for v in ir.read if not is_slithir_var(v)] self._vars_read += [v for v in ir.read if not is_slithir_var(v)]
if isinstance(ir, OperationWithLValue): if isinstance(ir, OperationWithLValue):
if isinstance(ir, (Index, Member)): if isinstance(ir, (Index, Member, Length, Balance)):
continue # Don't consider Member and Index operations -> ReferenceVariable continue # Don't consider Member and Index operations -> ReferenceVariable
var = ir.lvalue var = ir.lvalue
# If its a reference, we loop until finding the origin # If its a reference, we loop until finding the origin
@ -413,5 +409,3 @@ class Node(SourceMapping, ChildFunction):
self._high_level_calls = list(set(self._high_level_calls)) self._high_level_calls = list(set(self._high_level_calls))
self._low_level_calls = list(set(self._low_level_calls)) self._low_level_calls = list(set(self._low_level_calls))

@ -22,8 +22,7 @@ SOLIDITY_VARIABLES_COMPOSED = {"block.coinbase":"address",
"msg.sig":"bytes4", "msg.sig":"bytes4",
"msg.value":"uint256", "msg.value":"uint256",
"tx.gasprice":"uint256", "tx.gasprice":"uint256",
"tx.origin":"address", "tx.origin":"address"}
"this.balance":"uint256"}
SOLIDITY_FUNCTIONS = {"gasleft()":['uint256'], SOLIDITY_FUNCTIONS = {"gasleft()":['uint256'],

@ -3,7 +3,8 @@
""" """
from slither.core.source_mapping.source_mapping import SourceMapping from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.solidity_types.type import Type
from slither.core.solidity_types.elementary_type import ElementaryType
class Variable(SourceMapping): class Variable(SourceMapping):
@ -73,6 +74,9 @@ class Variable(SourceMapping):
return self._visibility return self._visibility
def set_type(self, t): def set_type(self, t):
if isinstance(t, str):
t = ElementaryType(t)
assert isinstance(t, (Type, list)) or t is None
self._type = t self._type = t
def __str__(self): def __str__(self):

@ -12,7 +12,7 @@ class OldSolc(AbstractDetector):
""" """
ARGUMENT = 'solc-version' ARGUMENT = 'solc-version'
HELP = 'If an old version of Solidity used (<0.4.23)' HELP = 'Old versions of Solidity (< 0.4.23)'
IMPACT = DetectorClassification.INFORMATIONAL IMPACT = DetectorClassification.INFORMATIONAL
CONFIDENCE = DetectorClassification.HIGH CONFIDENCE = DetectorClassification.HIGH

@ -27,7 +27,7 @@ class ArbitrarySend(AbstractDetector):
""" """
ARGUMENT = 'arbitrary-send' ARGUMENT = 'arbitrary-send'
HELP = 'Functions that send ether to an arbitrary destination' HELP = 'Functions that send ether to arbitrary destinations'
IMPACT = DetectorClassification.HIGH IMPACT = DetectorClassification.HIGH
CONFIDENCE = DetectorClassification.MEDIUM CONFIDENCE = DetectorClassification.MEDIUM

@ -12,7 +12,7 @@ class ExternalFunction(AbstractDetector):
""" """
ARGUMENT = 'external-function' ARGUMENT = 'external-function'
HELP = 'Detect public function that could be declared as external' HELP = 'Public function that could be declared as external'
IMPACT = DetectorClassification.INFORMATIONAL IMPACT = DetectorClassification.INFORMATIONAL
CONFIDENCE = DetectorClassification.HIGH CONFIDENCE = DetectorClassification.HIGH

@ -38,10 +38,11 @@ class UninitializedStateVarsDetection(AbstractDetector):
if isinstance(ir, LibraryCall) \ if isinstance(ir, LibraryCall) \
or isinstance(ir, InternalCall): or isinstance(ir, InternalCall):
idx = 0 idx = 0
for param in ir.function.parameters: if ir.function:
if param.location == 'storage': for param in ir.function.parameters:
ret.append(ir.arguments[idx]) if param.location == 'storage':
idx = idx+1 ret.append(ir.arguments[idx])
idx = idx+1
return ret return ret

@ -14,7 +14,7 @@ from slither.slithir.operations import (Assignment, Binary, BinaryType, Call,
NewStructure, OperationWithLValue, NewStructure, OperationWithLValue,
Push, Return, Send, SolidityCall, Push, Return, Send, SolidityCall,
Transfer, TypeConversion, Unary, Transfer, TypeConversion, Unary,
Unpack) Unpack, Length, Balance)
from slither.slithir.tmp_operations.argument import Argument, ArgumentType from slither.slithir.tmp_operations.argument import Argument, ArgumentType
from slither.slithir.tmp_operations.tmp_call import TmpCall from slither.slithir.tmp_operations.tmp_call import TmpCall
from slither.slithir.tmp_operations.tmp_new_array import TmpNewArray from slither.slithir.tmp_operations.tmp_new_array import TmpNewArray
@ -189,7 +189,10 @@ def convert_to_low_level(ir):
call = SolidityFunction('abi.{}()'.format(ir.function_name)) call = SolidityFunction('abi.{}()'.format(ir.function_name))
new_ir = SolidityCall(call, ir.nbr_arguments, ir.lvalue, ir.type_call) new_ir = SolidityCall(call, ir.nbr_arguments, ir.lvalue, ir.type_call)
new_ir.arguments = ir.arguments new_ir.arguments = ir.arguments
new_ir.lvalue.set_type(call.return_type) if isinstance(call.return_type, list) and len(call.return_type) == 1:
new_ir.lvalue.set_type(call.return_type[0])
else:
new_ir.lvalue.set_type(call.return_type)
return new_ir return new_ir
elif ir.function_name in ['call', 'delegatecall', 'callcode']: elif ir.function_name in ['call', 'delegatecall', 'callcode']:
new_ir = LowLevelCall(ir.destination, new_ir = LowLevelCall(ir.destination,
@ -254,9 +257,10 @@ def convert_to_library(ir, node, using_for):
contract = node.function.contract contract = node.function.contract
t = ir.destination.type t = ir.destination.type
new_ir = look_for_library(contract, ir, node, using_for, t) if t in using_for:
if new_ir: new_ir = look_for_library(contract, ir, node, using_for, t)
return new_ir if new_ir:
return new_ir
if '*' in using_for: if '*' in using_for:
new_ir = look_for_library(contract, ir, node, using_for, '*') new_ir = look_for_library(contract, ir, node, using_for, '*')
@ -353,7 +357,12 @@ def convert_type_of_high_level_call(ir, contract):
return_type = return_type[0] return_type = return_type[0]
else: else:
# otherwise its a variable (getter) # otherwise its a variable (getter)
return_type = func.type if isinstance(func.type, MappingType):
return_type = func.type.type_to
elif isinstance(func.type, ArrayType):
return_type = func.type.type
else:
return_type = func.type
if return_type: if return_type:
ir.lvalue.set_type(return_type) ir.lvalue.set_type(return_type)
else: else:
@ -365,6 +374,7 @@ def propagate_types(ir, node):
# propagate the type # propagate the type
using_for = node.function.contract.using_for using_for = node.function.contract.using_for
if isinstance(ir, OperationWithLValue): if isinstance(ir, OperationWithLValue):
# Force assignment in case of missing previous correct type
if not ir.lvalue.type: if not ir.lvalue.type:
if isinstance(ir, Assignment): if isinstance(ir, Assignment):
ir.lvalue.set_type(ir.rvalue.type) ir.lvalue.set_type(ir.rvalue.type)
@ -386,7 +396,7 @@ def propagate_types(ir, node):
return return
# convert library # convert library
if t in using_for: if t in using_for or '*' in using_for:
new_ir = convert_to_library(ir, node, using_for) new_ir = convert_to_library(ir, node, using_for)
if new_ir: if new_ir:
return new_ir return new_ir
@ -427,7 +437,7 @@ def propagate_types(ir, node):
if return_type: if return_type:
if len(return_type) == 1: if len(return_type) == 1:
ir.lvalue.set_type(return_type[0]) ir.lvalue.set_type(return_type[0])
else: elif len(return_type)>1:
ir.lvalue.set_type(return_type) ir.lvalue.set_type(return_type)
else: else:
ir.lvalue = None ir.lvalue = None
@ -446,6 +456,11 @@ def propagate_types(ir, node):
# This should not happen # This should not happen
assert False assert False
elif isinstance(ir, Member): elif isinstance(ir, Member):
# TODO we should convert the reference to a temporary if the member is a length or a balance
if ir.variable_right == 'length' and isinstance(ir.variable_left.type, (ElementaryType, ArrayType)):
return Length(ir.variable_left, ir.lvalue)
if ir.variable_right == 'balance' and isinstance(ir.variable_left.type, ElementaryType):
return Balance(ir.variable_left, ir.lvalue)
left = ir.variable_left left = ir.variable_left
if isinstance(left, (Variable, SolidityVariable)): if isinstance(left, (Variable, SolidityVariable)):
t = ir.variable_left.type t = ir.variable_left.type
@ -483,7 +498,7 @@ def propagate_types(ir, node):
return_type = ir.function.return_type return_type = ir.function.return_type
if len(return_type) == 1: if len(return_type) == 1:
ir.lvalue.set_type(return_type[0]) ir.lvalue.set_type(return_type[0])
else: elif len(return_type)>1:
ir.lvalue.set_type(return_type) ir.lvalue.set_type(return_type)
elif isinstance(ir, TypeConversion): elif isinstance(ir, TypeConversion):
ir.lvalue.set_type(ir.type) ir.lvalue.set_type(ir.type)

@ -26,3 +26,5 @@ from .transfer import Transfer
from .type_conversion import TypeConversion from .type_conversion import TypeConversion
from .unary import Unary, UnaryType from .unary import Unary, UnaryType
from .unpack import Unpack from .unpack import Unpack
from .length import Length
from .balance import Balance

@ -36,4 +36,4 @@ class Assignment(OperationWithLValue):
return self._rvalue return self._rvalue
def __str__(self): def __str__(self):
return '{} := {}'.format(self.lvalue, self.rvalue) return '{}({}) := {}({})'.format(self.lvalue, self.lvalue.type, self.rvalue, self.rvalue.type)

@ -0,0 +1,26 @@
import logging
from slither.slithir.operations.lvalue import OperationWithLValue
from slither.core.declarations import Function
from slither.core.variables.variable import Variable
from slither.slithir.utils.utils import is_valid_lvalue, is_valid_rvalue
from slither.core.solidity_types.elementary_type import ElementaryType
class Balance(OperationWithLValue):
def __init__(self, value, lvalue):
assert is_valid_rvalue(value)
assert is_valid_lvalue(lvalue)
self._value = value
self._lvalue = lvalue
lvalue.set_type(ElementaryType('uint256'))
@property
def read(self):
return [self._value]
@property
def value(self):
return self._value
def __str__(self):
return "{} -> BALANCE {}".format(self.lvalue, self.value)

@ -2,6 +2,7 @@ import logging
from slither.slithir.operations.lvalue import OperationWithLValue from slither.slithir.operations.lvalue import OperationWithLValue
from slither.core.variables.variable import Variable from slither.core.variables.variable import Variable
from slither.slithir.utils.utils import is_valid_lvalue, is_valid_rvalue from slither.slithir.utils.utils import is_valid_lvalue, is_valid_rvalue
from slither.core.solidity_types import ElementaryType
logger = logging.getLogger("BinaryOperationIR") logger = logging.getLogger("BinaryOperationIR")
@ -135,7 +136,7 @@ class Binary(OperationWithLValue):
self._type = operation_type self._type = operation_type
self._lvalue = result self._lvalue = result
if BinaryType.return_bool(operation_type): if BinaryType.return_bool(operation_type):
result.set_type('bool') result.set_type(ElementaryType('bool'))
else: else:
result.set_type(left_variable.type) result.set_type(left_variable.type)

@ -14,7 +14,15 @@ class EventCall(Call):
@property @property
def read(self): def read(self):
return list(self.arguments) def unroll(l):
ret = []
for x in l:
if not isinstance(x, list):
ret += [x]
else:
ret += unroll(x)
return ret
return unroll(self.arguments)
def __str__(self): def __str__(self):
args = [str(a) for a in self.arguments] args = [str(a) for a in self.arguments]

@ -58,7 +58,16 @@ class HighLevelCall(Call, OperationWithLValue):
@property @property
def read(self): def read(self):
all_read = [self.destination, self.call_gas, self.call_value] + self.arguments # if array inside the parameters
def unroll(l):
ret = []
for x in l:
if not isinstance(x, list):
ret += [x]
else:
ret += unroll(x)
return ret
all_read = [self.destination, self.call_gas, self.call_value] + unroll(self.arguments)
# remove None # remove None
return [x for x in all_read if x] return [x for x in all_read if x]

@ -23,7 +23,16 @@ class InitArray(OperationWithLValue):
@property @property
def read(self): def read(self):
return list(self.init_values) # if array inside the init values
def unroll(l):
ret = []
for x in l:
if not isinstance(x, list):
ret += [x]
else:
ret += unroll(x)
return ret
return unroll(self.init_values)
@property @property
def init_values(self): def init_values(self):

@ -16,7 +16,16 @@ class InternalCall(Call, OperationWithLValue):
@property @property
def read(self): def read(self):
return list(self.arguments) # if array inside the parameters
def unroll(l):
ret = []
for x in l:
if not isinstance(x, list):
ret += [x]
else:
ret += unroll(x)
return ret
return list(unroll(self.arguments))
@property @property
def function(self): def function(self):

@ -19,7 +19,17 @@ class InternalDynamicCall(Call, OperationWithLValue):
@property @property
def read(self): def read(self):
return list(self.arguments) + [self.function] # if array inside the parameters
def unroll(l):
ret = []
for x in l:
if not isinstance(x, list):
ret += [x]
else:
ret += unroll(x)
return ret
return unroll(self.arguments) + [self.function]
@property @property
def function(self): def function(self):

@ -0,0 +1,26 @@
import logging
from slither.slithir.operations.lvalue import OperationWithLValue
from slither.core.declarations import Function
from slither.core.variables.variable import Variable
from slither.slithir.utils.utils import is_valid_lvalue, is_valid_rvalue
from slither.core.solidity_types.elementary_type import ElementaryType
class Length(OperationWithLValue):
def __init__(self, value, lvalue):
assert is_valid_rvalue(value)
assert is_valid_lvalue(lvalue)
self._value = value
self._lvalue = lvalue
lvalue.set_type(ElementaryType('uint256'))
@property
def read(self):
return [self._value]
@property
def value(self):
return self._value
def __str__(self):
return "{} -> LENGTH {}".format(self.lvalue, self.value)

@ -18,7 +18,16 @@ class NewArray(Call, OperationWithLValue):
@property @property
def read(self): def read(self):
return list(self.arguments) # if array inside the parameters
def unroll(l):
ret = []
for x in l:
if not isinstance(x, list):
ret += [x]
else:
ret += unroll(x)
return ret
return unroll(self.arguments)
@property @property
def depth(self): def depth(self):

@ -39,7 +39,17 @@ class NewContract(Call, OperationWithLValue):
@property @property
def read(self): def read(self):
return list(self.arguments) # if array inside the parameters
def unroll(l):
ret = []
for x in l:
if not isinstance(x, list):
ret += [x]
else:
ret += unroll(x)
return ret
return unroll(self.arguments)
def __str__(self): def __str__(self):
value = '' value = ''
if self.call_value: if self.call_value:

@ -17,7 +17,16 @@ class NewStructure(Call, OperationWithLValue):
@property @property
def read(self): def read(self):
return list(self.arguments) # if array inside the parameters
def unroll(l):
ret = []
for x in l:
if not isinstance(x, list):
ret += [x]
else:
ret += unroll(x)
return ret
return unroll(self.arguments)
@property @property
def structure(self): def structure(self):

@ -1,4 +1,5 @@
from slither.core.variables.variable import Variable from slither.core.variables.variable import Variable
from slither.core.solidity_types.elementary_type import ElementaryType
class Constant(Variable): class Constant(Variable):
@ -6,10 +7,10 @@ class Constant(Variable):
super(Constant, self).__init__() super(Constant, self).__init__()
assert isinstance(val, str) assert isinstance(val, str)
if val.isdigit(): if val.isdigit():
self._type = 'uint256' self._type = ElementaryType('uint256')
self._val = int(val) self._val = int(val)
else: else:
self._type = 'string' self._type = ElementaryType('string')
self._val = val self._val = val
@property @property

@ -1,6 +1,7 @@
from slither.core.variables.variable import Variable from slither.core.variables.variable import Variable
from slither.core.solidity_types.type import Type
class TupleVariable(Variable): class TupleVariable(Variable):
COUNTER = 0 COUNTER = 0

@ -152,11 +152,11 @@ def parse_call(expression, caller_context):
type_info = children[0] type_info = children[0]
expression_to_parse = children[1] expression_to_parse = children[1]
assert type_info['name'] in ['ElementaryTypenameExpression', assert type_info['name'] in ['ElementaryTypenameExpression',
'ElementaryTypeNameExpression', 'ElementaryTypeNameExpression',
'Identifier', 'Identifier',
'TupleExpression', 'TupleExpression',
'IndexAccess', 'IndexAccess',
'MemberAccess'] 'MemberAccess']
expression = parse_expression(expression_to_parse, caller_context) expression = parse_expression(expression_to_parse, caller_context)
t = TypeConversion(expression, type_call) t = TypeConversion(expression, type_call)

Loading…
Cancel
Save