Merge branch 'dev' into feature/rewrite-uninitialized-state-with-ir

pull/49/head
Feist Josselin 6 years ago committed by GitHub
commit b81b137481
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 107
      scripts/travis_test.sh
  2. 40
      slither/__main__.py
  3. 105
      slither/core/cfg/node.py
  4. 74
      slither/core/declarations/function.py
  5. 21
      slither/slither.py
  6. 12
      slither/slithir/convert.py
  7. 5
      slither/slithir/operations/unpack.py
  8. 5
      slither/slithir/variables/reference.py
  9. 26
      slither/solc_parsing/cfg/node.py
  10. 101
      slither/solc_parsing/declarations/contract.py
  11. 18
      slither/solc_parsing/declarations/event.py
  12. 388
      slither/solc_parsing/declarations/function.py
  13. 17
      slither/solc_parsing/declarations/modifier.py
  14. 176
      slither/solc_parsing/expressions/expression_parsing.py
  15. 61
      slither/solc_parsing/slitherSolc.py
  16. 53
      slither/solc_parsing/solidity_types/type_parsing.py
  17. 46
      slither/solc_parsing/variables/variable_declaration.py
  18. 35
      slither/utils/expression_manipulations.py

@ -2,84 +2,35 @@
### Test Detectors
slither tests/uninitialized.sol --disable-solc-warnings --detect-uninitialized-state
if [ $? -ne 3 ]; then
exit 1
fi
# contains also the test for the suicidal detector
slither tests/backdoor.sol --disable-solc-warnings --detect-backdoor
if [ $? -ne 1 ]; then
exit 1
fi
slither tests/pragma.0.4.24.sol --disable-solc-warnings --detect-pragma
if [ $? -ne 1 ]; then
exit 1
fi
slither tests/old_solc.sol.json --solc-ast --detect-solc-version
if [ $? -ne 1 ]; then
exit 1
fi
slither tests/reentrancy.sol --disable-solc-warnings --detect-reentrancy
if [ $? -ne 1 ]; then
exit 1
fi
slither tests/uninitialized_storage_pointer.sol --disable-solc-warnings --detect-uninitialized-storage
if [ $? -ne 1 ]; then
exit 1
fi
slither tests/tx_origin.sol --disable-solc-warnings --detect-tx-origin
if [ $? -ne 2 ]; then
exit 1
fi
slither tests/unused_state.sol --detect-unused-state
if [ $? -ne 1 ]; then
exit 1
fi
slither tests/locked_ether.sol --detect-locked-ether
if [ $? -ne 1 ]; then
exit 1
fi
slither tests/arbitrary_send.sol --disable-solc-warnings --detect-arbitrary-send
if [ $? -ne 2 ]; then
exit 1
fi
slither tests/inline_assembly_contract.sol --disable-solc-warnings --detect-assembly
if [ $? -ne 1 ]; then
exit 1
fi
slither tests/inline_assembly_library.sol --disable-solc-warnings --detect-assembly
if [ $? -ne 2 ]; then
exit 1
fi
slither tests/naming_convention.sol --disable-solc-warnings --detect-naming-convention
if [ $? -ne 10 ]; then
exit 1
fi
slither tests/low_level_calls.sol --disable-solc-warnings --detect-low-level-calls
if [ $? -ne 1 ]; then
exit 1
fi
slither tests/const_state_variables.sol --detect-const-candidates-state
if [ $? -ne 2 ]; then
exit 1
fi
# test_slither file.sol --detect-detectors number_results
test_slither(){
slither "$1" --disable-solc-warnings "$2"
if [ $? -ne "$3" ]; then
exit 1
fi
slither "$1" --disable-solc-warnings "$2" --compact-ast
if [ $? -ne "$3" ]; then
exit 1
fi
}
test_slither tests/uninitialized.sol "--detect-uninitialized-state" 1
test_slither tests/backdoor.sol "--detect-backdoor" 1
test_slither tests/backdoor.sol "--detect-suicidal" 1
test_slither tests/pragma.0.4.24.sol "--detect-pragma" 1
test_slither tests/old_solc.sol.json "--detect-solc-version" 1
test_slither tests/reentrancy.sol "--detect-reentrancy" 1
test_slither tests/uninitialized_storage_pointer.sol "--detect-uninitialized-storage" 1
test_slither tests/tx_origin.sol "--detect-tx-origin" 2
test_slither tests/unused_state.sol "--detect-unused-state" 1
test_slither tests/locked_ether.sol "--detect-locked-ether" 1
test_slither tests/arbitrary_send.sol "--detect-arbitrary-send" 2
test_slither tests/inline_assembly_contract.sol "--detect-assembly" 1
test_slither tests/inline_assembly_library.sol "--detect-assembly" 2
test_slither tests/naming_convention.sol "--detect-naming-convention" 10
test_slither tests/low_level_calls.sol "--detect-low-level-calls" 1
test_slither tests/const_state_variables.sol "--detect-const-candidates-state" 2
### Test scripts

@ -15,6 +15,7 @@ from slither.detectors.abstract_detector import (AbstractDetector,
classification_txt)
from slither.printers.abstract_printer import AbstractPrinter
from slither.slither import Slither
from slither.utils.colors import red
logging.basicConfig()
logger = logging.getLogger("Slither")
@ -52,8 +53,14 @@ def process(filename, args, detector_classes, printer_classes):
Returns:
list(result), int: Result list and number of contracts analyzed
"""
slither = Slither(filename, args.solc, args.disable_solc_warnings, args.solc_args)
ast = '--ast-json'
if args.compact_ast:
ast = '--ast-compact-json'
slither = Slither(filename, args.solc, args.disable_solc_warnings, args.solc_args, ast)
return _process(slither, detector_classes, printer_classes)
def _process(slither, detector_classes, printer_classes):
for detector_cls in detector_classes:
slither.register_detector(detector_cls)
@ -76,6 +83,28 @@ def process(filename, args, detector_classes, printer_classes):
return results, analyzed_contracts_count
def process_truffle(dirname, args, detector_classes, printer_classes):
if not os.path.isdir(os.path.join(dirname, 'build'))\
or not os.path.isdir(os.path.join(dirname, 'build', 'contracts')):
logger.info(red('No truffle build directory found, did you run `truffle compile`?'))
return (0,0)
filenames = glob.glob(os.path.join(dirname,'build','contracts', '*.json'))
all_contracts = []
for filename in filenames:
with open(filename) as f:
contract_loaded = json.load(f)
all_contracts += contract_loaded['ast']['nodes']
contract = {
"nodeType": "SourceUnit",
"nodes" : all_contracts}
slither = Slither(contract, args.solc, args.disable_solc_warnings, args.solc_args)
return _process(slither, detector_classes, printer_classes)
def output_json(results, filename):
with open(filename, 'w') as f:
@ -194,6 +223,9 @@ def main_impl(all_detector_classes, all_printer_classes):
if os.path.isfile(filename):
(results, number_contracts) = process(filename, args, detector_classes, printer_classes)
elif os.path.isfile(os.path.join(filename, 'truffle.js')):
(results, number_contracts) = process_truffle(filename, args, detector_classes, printer_classes)
elif os.path.isdir(filename) or len(globbed_filenames) > 0:
extension = "*.sol" if not args.solc_ast else "*.json"
filenames = glob.glob(os.path.join(filename, extension))
@ -209,6 +241,7 @@ def main_impl(all_detector_classes, all_printer_classes):
# output_json(results, args.json)
# exit(results)
else:
raise Exception("Unrecognised file/dir path: '#{filename}'".format(filename=filename))
@ -319,6 +352,11 @@ def parse_args(detector_classes, printer_classes):
action="store_true",
default=False)
parser.add_argument('--compact-ast',
help=argparse.SUPPRESS,
action='store_true',
default=False)
return parser.parse_args()

@ -5,6 +5,7 @@ import logging
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.visitors.expression.expression_printer import ExpressionPrinter
from slither.visitors.expression.read_var import ReadVar
@ -12,10 +13,17 @@ from slither.visitors.expression.write_var import WriteVar
from slither.core.children.child_function import ChildFunction
from slither.core.declarations.solidity_variables import SolidityFunction
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")
class NodeType:
@ -103,7 +111,10 @@ class Node(SourceMapping, ChildFunction):
self._vars_written = []
self._vars_read = []
self._internal_calls = []
self._external_calls = []
self._solidity_calls = []
self._high_level_calls = []
self._low_level_calls = []
self._external_calls_as_expressions = []
self._irs = []
self._state_vars_written = []
@ -176,16 +187,43 @@ class Node(SourceMapping, ChildFunction):
@property
def internal_calls(self):
"""
list(Function or SolidityFunction): List of function calls (that does not create a transaction)
list(Function or SolidityFunction): List of internal/soldiity function calls
"""
return list(self._internal_calls)
@property
def external_calls(self):
def solidity_calls(self):
"""
list(SolidityFunction): List of Soldity calls
"""
return list(self._internal_calls)
@property
def high_level_calls(self):
"""
list((Contract, Function|Variable)):
List of high level calls (external calls).
A variable is called in case of call to a public state variable
Include library calls
"""
return list(self._high_level_calls)
@property
def low_level_calls(self):
"""
list((Variable|SolidityVariable, str)): List of low_level call
A low level call is defined by
- the variable called
- the name of the function (call/delegatecall/codecall)
"""
return list(self._low_level_calls)
@property
def external_calls_as_expressions(self):
"""
list(CallExpression): List of message calls (that creates a transaction)
"""
return self._external_calls
return self._external_calls_as_expressions
@property
def calls_as_expression(self):
@ -226,10 +264,7 @@ class Node(SourceMapping, ChildFunction):
Returns:
bool: True if the node has a require or assert call
"""
return self.internal_calls and\
any(isinstance(c, SolidityFunction) and\
(c.name in ['require(bool)', 'require(bool,string)', 'assert(bool)'])\
for c in self.internal_calls)
return any(c.name in ['require(bool)', 'require(bool,string)', 'assert(bool)'] for c in self.internal_calls)
def contains_if(self):
"""
@ -328,3 +363,55 @@ class Node(SourceMapping, ChildFunction):
if self.expression:
expression = self.expression
self._irs = convert_expression(expression, self)
self._find_read_write_call()
def _find_read_write_call(self):
def is_slithir_var(var):
return isinstance(var, (Constant, ReferenceVariable, TemporaryVariable, TupleVariable))
for ir in self.irs:
self._vars_read += [v for v in ir.read if not is_slithir_var(v)]
if isinstance(ir, OperationWithLValue):
if isinstance(ir, (Index, Member)):
continue # Don't consider Member and Index operations -> ReferenceVariable
var = ir.lvalue
# If its a reference, we loop until finding the origin
if isinstance(var, (ReferenceVariable)):
while isinstance(var, ReferenceVariable):
var = var.points_to
# Only store non-slithIR variables
if not is_slithir_var(var):
self._vars_written.append(var)
if isinstance(ir, InternalCall):
self._internal_calls.append(ir.function)
if isinstance(ir, SolidityCall):
# TODO: consider removing dependancy of solidity_call to internal_call
self._solidity_calls.append(ir.function)
self._internal_calls.append(ir.function)
if isinstance(ir, LowLevelCall):
assert isinstance(ir.destination, (Variable, SolidityVariable))
self._low_level_calls.append((ir.destination, ir.function_name.value))
elif isinstance(ir, (HighLevelCall)) and not isinstance(ir, LibraryCall):
if isinstance(ir.destination.type, Contract):
self._high_level_calls.append((ir.destination.type, ir.function))
else:
self._high_level_calls.append((ir.destination.type.type, ir.function))
elif isinstance(ir, LibraryCall):
assert isinstance(ir.destination, Contract)
self._high_level_calls.append((ir.destination, ir.function))
self._vars_read = list(set(self._vars_read))
self._state_vars_read = [v for v in self._vars_read if isinstance(v, StateVariable)]
self._solidity_vars_read = [v for v in self._vars_read if isinstance(v, SolidityVariable)]
self._vars_written = list(set(self._vars_written))
self._state_vars_written = [v for v in self._vars_written if isinstance(v, StateVariable)]
self._internal_calls = list(set(self._internal_calls))
self._solidity_calls = list(set(self._solidity_calls))
self._high_level_calls = list(set(self._high_level_calls))
self._low_level_calls = list(set(self._low_level_calls))

@ -44,7 +44,10 @@ class Function(ChildContract, SourceMapping):
self._solidity_vars_read = []
self._state_vars_written = []
self._internal_calls = []
self._external_calls = []
self._solidity_calls = []
self._low_level_calls = []
self._high_level_calls = []
self._external_calls_as_expressions = []
self._expression_vars_read = []
self._expression_vars_written = []
self._expression_calls = []
@ -239,14 +242,42 @@ class Function(ChildContract, SourceMapping):
return list(self._internal_calls)
@property
def external_calls(self):
def solidity_calls(self):
"""
list(SolidityFunction): List of Soldity calls
"""
return list(self._internal_calls)
@property
def high_level_calls(self):
"""
list((Contract, Function|Variable)):
List of high level calls (external calls).
A variable is called in case of call to a public state variable
Include library calls
"""
return list(self._high_level_calls)
@property
def low_level_calls(self):
"""
list((Variable|SolidityVariable, str)): List of low_level call
A low level call is defined by
- the variable called
- the name of the function (call/delegatecall/codecall)
"""
return list(self._low_level_calls)
@property
def external_calls_as_expressions(self):
"""
list(ExpressionCall): List of message calls (that creates a transaction)
"""
return list(self._external_calls)
return list(self._external_calls_as_expressions)
@property
def calls_as_expression(self):
def calls_as_expressions(self):
return self._expression_calls
@property
@ -353,6 +384,7 @@ class Function(ChildContract, SourceMapping):
calls = [x for x in calls if x]
calls = [item for sublist in calls for item in sublist]
# Remove dupplicate if they share the same string representation
# TODO: check if groupby is still necessary here
calls = [next(obj) for i, obj in\
groupby(sorted(calls, key=lambda x: str(x)), lambda x: str(x))]
self._expression_calls = calls
@ -364,12 +396,30 @@ class Function(ChildContract, SourceMapping):
groupby(sorted(internal_calls, key=lambda x: str(x)), lambda x: str(x))]
self._internal_calls = internal_calls
external_calls = [x.external_calls for x in self.nodes]
external_calls = [x for x in external_calls if x]
external_calls = [item for sublist in external_calls for item in sublist]
external_calls = [next(obj) for i, obj in
groupby(sorted(external_calls, key=lambda x: str(x)), lambda x: str(x))]
self._external_calls = external_calls
self._solidity_calls = [c for c in internal_calls if isinstance(c, SolidityFunction)]
low_level_calls = [x.low_level_calls for x in self.nodes]
low_level_calls = [x for x in low_level_calls if x]
low_level_calls = [item for sublist in low_level_calls for item in sublist]
low_level_calls = [next(obj) for i, obj in
groupby(sorted(low_level_calls, key=lambda x: str(x)), lambda x: str(x))]
self._low_level_calls = low_level_calls
high_level_calls = [x.high_level_calls for x in self.nodes]
high_level_calls = [x for x in high_level_calls if x]
high_level_calls = [item for sublist in high_level_calls for item in sublist]
high_level_calls = [next(obj) for i, obj in
groupby(sorted(high_level_calls, key=lambda x: str(x)), lambda x: str(x))]
self._high_level_calls = high_level_calls
external_calls_as_expressions = [x.external_calls_as_expressions for x in self.nodes]
external_calls_as_expressions = [x for x in external_calls_as_expressions if x]
external_calls_as_expressions = [item for sublist in external_calls_as_expressions for item in sublist]
external_calls_as_expressions = [next(obj) for i, obj in
groupby(sorted(external_calls_as_expressions, key=lambda x: str(x)), lambda x: str(x))]
self._external_calls_as_expressions = external_calls_as_expressions
def _explore_functions(self, f_new_values):
@ -567,14 +617,14 @@ class Function(ChildContract, SourceMapping):
Return the function summary
Returns:
(str, str, list(str), list(str), listr(str), list(str), list(str);
name, visibility, modifiers, vars read, vars written, internal_calls, external_calls
name, visibility, modifiers, vars read, vars written, internal_calls, external_calls_as_expressions
"""
return (self.name, self.visibility,
[str(x) for x in self.modifiers],
[str(x) for x in self.state_variables_read + self.solidity_variables_read],
[str(x) for x in self.state_variables_written],
[str(x) for x in self.internal_calls],
[str(x) for x in self.external_calls])
[str(x) for x in self.external_calls_as_expressions])
def is_protected(self):
"""

@ -17,16 +17,21 @@ logger_printer = logging.getLogger("Printers")
class Slither(SlitherSolc):
def __init__(self, filename, solc='solc', disable_solc_warnings=False, solc_arguments=''):
def __init__(self, contract, solc='solc', disable_solc_warnings=False, solc_arguments='', ast_format='--ast-json'):
self._detectors = []
self._printers = []
stdout = self._run_solc(filename, solc, disable_solc_warnings, solc_arguments)
super(Slither, self).__init__(filename)
# json text provided
if isinstance(contract, dict):
super(Slither, self).__init__('')
self._parse_contracts_from_loaded_json(contract, '')
# .json or .sol provided
else:
contracts_json = self._run_solc(contract, solc, disable_solc_warnings, solc_arguments, ast_format)
super(Slither, self).__init__(contract)
for d in stdout:
self._parse_contracts_from_json(d)
for c in contracts_json:
self._parse_contracts_from_json(c)
self._analyze_contracts()
@ -76,7 +81,7 @@ class Slither(SlitherSolc):
"You can't register {!r} twice.".format(cls)
)
def _run_solc(self, filename, solc, disable_solc_warnings, solc_arguments):
def _run_solc(self, filename, solc, disable_solc_warnings, solc_arguments, ast_format):
if not os.path.isfile(filename):
logger.error('{} does not exist (are you in the correct directory?)'.format(filename))
exit(-1)
@ -93,7 +98,7 @@ class Slither(SlitherSolc):
logger.info('Empty AST file: %s', filename)
sys.exit(-1)
else:
cmd = [solc, filename, '--ast-json']
cmd = [solc, filename, ast_format]
if solc_arguments:
# To parse, we first split the string on each '--'
solc_args = solc_arguments.split('--')

@ -283,6 +283,8 @@ def get_sig(ir):
for arg in ir.arguments:
if isinstance(arg, (list,)):
type_arg = '{}[{}]'.format(get_type(arg[0].type), len(arg))
elif isinstance(arg, Function):
type_arg = arg.signature_str
else:
type_arg = get_type(arg.type)
args.append(type_arg)
@ -544,8 +546,14 @@ def remove_temporary(result):
return result
def remove_unused(result):
removed = True
if not result:
return result
# dont remove the last elem, as it may be used by RETURN
last_elem = result[-1]
while removed:
removed = False
@ -562,7 +570,7 @@ def remove_unused(result):
for ins in result:
if isinstance(ins, Member):
if not ins.lvalue.name in to_keep:
if not ins.lvalue.name in to_keep and ins != last_elem:
to_remove.append(ins)
removed = True

@ -28,4 +28,7 @@ class Unpack(OperationWithLValue):
return self._idx
def __str__(self):
return "{} = UNPACK {} index: {} ".format(self.lvalue, self.tuple, self.index)
return "{}({})= UNPACK {} index: {} ".format(self.lvalue,
self.lvalue.type,
self.tuple,
self.index)

@ -1,6 +1,6 @@
from slither.core.children.child_node import ChildNode
from slither.core.declarations import Contract, Enum, SolidityVariableComposed
from slither.core.declarations import Contract, Enum, SolidityVariable
from slither.core.variables.variable import Variable
@ -36,8 +36,7 @@ class ReferenceVariable(ChildNode, Variable):
# Member or Index operator
from slither.slithir.utils.utils import is_valid_lvalue
assert is_valid_lvalue(points_to) \
or points_to == SolidityVariableComposed('msg.data') \
or isinstance(points_to, (Contract, Enum))
or isinstance(points_to, (SolidityVariable, Contract, Enum))
self._points_to = points_to

@ -45,25 +45,21 @@ class NodeSolc(Node):
expression = self.expression
pp = ReadVar(expression)
self._expression_vars_read = pp.result()
vars_read = [ExportValues(v).result() for v in self._expression_vars_read]
self._vars_read = [item for sublist in vars_read for item in sublist]
self._state_vars_read = [x for x in self.variables_read if\
isinstance(x, (StateVariable))]
self._solidity_vars_read = [x for x in self.variables_read if\
isinstance(x, (SolidityVariable))]
# self._vars_read = [item for sublist in vars_read for item in sublist]
# self._state_vars_read = [x for x in self.variables_read if\
# isinstance(x, (StateVariable))]
# self._solidity_vars_read = [x for x in self.variables_read if\
# isinstance(x, (SolidityVariable))]
pp = WriteVar(expression)
self._expression_vars_written = pp.result()
vars_written = [ExportValues(v).result() for v in self._expression_vars_written]
self._vars_written = [item for sublist in vars_written for item in sublist]
self._state_vars_written = [x for x in self.variables_written if\
isinstance(x, StateVariable)]
# self._vars_written = [item for sublist in vars_written for item in sublist]
# self._state_vars_written = [x for x in self.variables_written if\
# isinstance(x, StateVariable)]
pp = FindCalls(expression)
self._expression_calls = pp.result()
calls = [ExportValues(c).result() for c in self.calls_as_expression]
calls = [item for sublist in calls for item in sublist]
self._internal_calls = [c for c in calls if isinstance(c, (Function, SolidityFunction))]
self._external_calls = [c for c in self.calls_as_expression if not isinstance(c.called, Identifier)]
self._external_calls_as_expressions = [c for c in self.calls_as_expression if not isinstance(c.called, Identifier)]

@ -37,8 +37,13 @@ class ContractSolc04(Contract):
self._is_analyzed = False
# Export info
self._name = self._data['attributes']['name']
if self.is_compact_ast:
self._name = self._data['name']
else:
self._name = self._data['attributes'][self.get_key()]
self._id = self._data['id']
self._inheritance = []
self._parse_contract_info()
@ -48,11 +53,27 @@ class ContractSolc04(Contract):
def is_analyzed(self):
return self._is_analyzed
def get_key(self):
return self.slither.get_key()
def get_children(self, key='nodes'):
if self.is_compact_ast:
return key
return 'children'
@property
def is_compact_ast(self):
return self.slither.is_compact_ast
def set_is_analyzed(self, is_analyzed):
self._is_analyzed = is_analyzed
def _parse_contract_info(self):
if self.is_compact_ast:
attributes = self._data
else:
attributes = self._data['attributes']
self.isInterface = False
if 'contractKind' in attributes:
if attributes['contractKind'] == 'interface':
@ -62,29 +83,29 @@ class ContractSolc04(Contract):
self.fullyImplemented = attributes['fullyImplemented']
def _parse_contract_items(self):
if not 'children' in self._data: # empty contract
if not self.get_children() in self._data: # empty contract
return
for item in self._data['children']:
if item['name'] == 'FunctionDefinition':
for item in self._data[self.get_children()]:
if item[self.get_key()] == 'FunctionDefinition':
self._functionsNotParsed.append(item)
elif item['name'] == 'EventDefinition':
elif item[self.get_key()] == 'EventDefinition':
self._eventsNotParsed.append(item)
elif item['name'] == 'InheritanceSpecifier':
elif item[self.get_key()] == 'InheritanceSpecifier':
# we dont need to parse it as it is redundant
# with self.linearizedBaseContracts
continue
elif item['name'] == 'VariableDeclaration':
elif item[self.get_key()] == 'VariableDeclaration':
self._variablesNotParsed.append(item)
elif item['name'] == 'EnumDefinition':
elif item[self.get_key()] == 'EnumDefinition':
self._enumsNotParsed.append(item)
elif item['name'] == 'ModifierDefinition':
elif item[self.get_key()] == 'ModifierDefinition':
self._modifiersNotParsed.append(item)
elif item['name'] == 'StructDefinition':
elif item[self.get_key()] == 'StructDefinition':
self._structuresNotParsed.append(item)
elif item['name'] == 'UsingForDirective':
elif item[self.get_key()] == 'UsingForDirective':
self._usingForNotParsed.append(item)
else:
logger.error('Unknown contract item: '+item['name'])
logger.error('Unknown contract item: '+item[self.get_key()])
exit(-1)
return
@ -92,8 +113,19 @@ class ContractSolc04(Contract):
for father in self.inheritance:
self._using_for.update(father.using_for)
if self.is_compact_ast:
for using_for in self._usingForNotParsed:
children = using_for['children']
lib_name = parse_type(using_for['libraryName'], self)
if 'typeName' in using_for and using_for['typeName']:
type_name = parse_type(using_for['typeName'], self)
else:
type_name = '*'
if not type_name in self._using_for:
self.using_for[type_name] = []
self._using_for[type_name].append(lib_name)
else:
for using_for in self._usingForNotParsed:
children = using_for[self.get_children()]
assert children and len(children) <= 2
if len(children) == 2:
new = parse_type(children[0], self)
@ -119,15 +151,22 @@ class ContractSolc04(Contract):
def _analyze_enum(self, enum):
# Enum can be parsed in one pass
name = enum['attributes']['name']
if self.is_compact_ast:
name = enum['name']
canonicalName = enum['canonicalName']
else:
name = enum['attributes'][self.get_key()]
if 'canonicalName' in enum['attributes']:
canonicalName = enum['attributes']['canonicalName']
else:
canonicalName = self.name + '.' + name
values = []
for child in enum['children']:
assert child['name'] == 'EnumValue'
values.append(child['attributes']['name'])
for child in enum[self.get_children('members')]:
assert child[self.get_key()] == 'EnumValue'
if self.is_compact_ast:
values.append(child['name'])
else:
values.append(child['attributes'][self.get_key()])
new_enum = Enum(name, canonicalName, values)
new_enum.set_contract(self)
@ -135,14 +174,19 @@ class ContractSolc04(Contract):
self._enums[canonicalName] = new_enum
def _parse_struct(self, struct):
name = struct['attributes']['name']
if 'canonicalName' in struct['attributes']:
canonicalName = struct['attributes']['canonicalName']
if self.is_compact_ast:
name = struct['name']
attributes = struct
else:
name = struct['attributes'][self.get_key()]
attributes = struct['attributes']
if 'canonicalName' in attributes:
canonicalName = attributes['canonicalName']
else:
canonicalName = self.name + '.' + name
if 'children' in struct:
children = struct['children']
if self.get_children('members') in struct:
children = struct[self.get_children('members')]
else:
children = [] # empty struct
st = StructureSolc(name, canonicalName, children)
@ -171,7 +215,7 @@ class ContractSolc04(Contract):
self._events.update(father.events_as_dict())
for event_to_parse in self._eventsNotParsed:
event = EventSolc(event_to_parse)
event = EventSolc(event_to_parse, self)
event.analyze(self)
event.set_contract(self)
self._events[event.full_name] = event
@ -189,6 +233,12 @@ class ContractSolc04(Contract):
self._variables[var.name] = var
def analyze_constant_state_variables(self):
for var in self.variables:
if var.is_constant:
var.analyze(self)
return
def analyze_state_variables(self):
for var in self.variables:
var.analyze(self)
@ -196,7 +246,7 @@ class ContractSolc04(Contract):
def _parse_modifier(self, modifier):
modif = ModifierSolc(modifier)
modif = ModifierSolc(modifier, self)
modif.set_contract(self)
modif.set_offset(modifier['src'], self.slither)
self._modifiers_no_params.append(modif)
@ -210,8 +260,7 @@ class ContractSolc04(Contract):
return
def _parse_function(self, function):
func = FunctionSolc(function)
func.set_contract(self)
func = FunctionSolc(function, self)
func.set_offset(function['src'], self.slither)
self._functions_no_params.append(func)

@ -9,18 +9,30 @@ class EventSolc(Event):
Event class
"""
def __init__(self, event):
def __init__(self, event, contract):
super(EventSolc, self).__init__()
self._name = event['attributes']['name']
self._elems = []
self._contract = contract
self._elems = []
if self.is_compact_ast:
self._name = event['name']
elems = event['parameters']
assert elems['nodeType'] == 'ParameterList'
self._elemsNotParsed = elems['parameters']
else:
self._name = event['attributes']['name']
elems = event['children'][0]
assert elems['name'] == 'ParameterList'
if 'children' in elems:
self._elemsNotParsed = elems['children']
else:
self._elemsNotParsed = []
@property
def is_compact_ast(self):
return self.contract.is_compact_ast
def analyze(self, contract):
for elem_to_parse in self._elemsNotParsed:
elem = EventVariableSolc(elem_to_parse)

@ -29,25 +29,46 @@ class FunctionSolc(Function):
"""
# elems = [(type, name)]
def __init__(self, function):
def __init__(self, function, contract):
super(FunctionSolc, self).__init__()
self._name = function['attributes']['name']
self._contract = contract
if self.is_compact_ast:
self._name = function['name']
else:
self._name = function['attributes'][self.get_key()]
self._functionNotParsed = function
self._params_was_analyzed = False
self._content_was_analyzed = False
self._counter_nodes = 0
def get_key(self):
return self.slither.get_key()
def get_children(self, key):
if self.is_compact_ast:
return key
return 'children'
@property
def is_compact_ast(self):
return self.slither.is_compact_ast
def _analyze_attributes(self):
if self.is_compact_ast:
attributes = self._functionNotParsed
else:
attributes = self._functionNotParsed['attributes']
if 'payable' in attributes:
self._payable = attributes['payable']
elif 'stateMutability' in attributes:
if 'stateMutability' in attributes:
if attributes['stateMutability'] == 'payable':
self._payable = True
elif attributes['stateMutability'] == 'pure':
self._pure = True
self._view = True
elif attributes['stateMutability'] == 'view':
self._view = True
if 'constant' in attributes:
self._view = attributes['constant']
@ -80,43 +101,79 @@ class FunctionSolc(Function):
def _parse_if(self, ifStatement, node):
# IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )?
falseStatement = None
children = ifStatement['children']
if self.is_compact_ast:
condition = ifStatement['condition']
# Note: check if the expression could be directly
# parsed here
condition_node = self._new_node(NodeType.IF)
#condition = parse_expression(children[0], self)
condition_node.add_unparsed_expression(condition)
link_nodes(node, condition_node)
trueStatement = self._parse_statement(ifStatement['trueBody'], condition_node)
if ifStatement['falseBody']:
falseStatement = self._parse_statement(ifStatement['falseBody'], condition_node)
else:
children = ifStatement[self.get_children('children')]
condition = children[0]
# Note: check if the expression could be directly
# parsed here
condition_node = self._new_node(NodeType.IF)
condition_node.add_unparsed_expression(condition)
link_nodes(node, condition_node)
trueStatement = self._parse_statement(children[1], condition_node)
if len(children) == 3:
falseStatement = self._parse_statement(children[2], condition_node)
endIf_node = self._new_node(NodeType.ENDIF)
link_nodes(trueStatement, endIf_node)
if len(children) == 3:
falseStatement = self._parse_statement(children[2], condition_node)
if falseStatement:
link_nodes(falseStatement, endIf_node)
else:
link_nodes(condition_node, endIf_node)
return endIf_node
# def _parse_if(self, ifStatement, node):
# # IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )?
#
# children = ifStatement[self.get_children('children')]
# condition_node = self._new_node(NodeType.IF)
# #condition = parse_expression(children[0], self)
# condition = children[0]
# condition_node.add_unparsed_expression(condition)
#
# link_nodes(node, condition_node)
#
# trueStatement = self._parse_statement(children[1], condition_node)
#
#
# endIf_node = self._new_node(NodeType.ENDIF)
# link_nodes(trueStatement, endIf_node)
#
# if len(children) == 3:
# falseStatement = self._parse_statement(children[2], condition_node)
#
# link_nodes(falseStatement, endIf_node)
#
# else:
# link_nodes(condition_node, endIf_node)
#
# return endIf_node
def _parse_while(self, whileStatement, node):
# WhileStatement = 'while' '(' Expression ')' Statement
children = whileStatement['children']
node_startWhile = self._new_node(NodeType.STARTLOOP)
node_condition = self._new_node(NodeType.IFLOOP)
#expression = parse_expression(children[0], self)
if self.is_compact_ast:
node_condition.add_unparsed_expression(whileStatement['condition'])
statement = self._parse_statement(whileStatement['body'], node_condition)
else:
children = whileStatement[self.get_children('children')]
expression = children[0]
node_condition.add_unparsed_expression(expression)
statement = self._parse_statement(children[1], node_condition)
node_endWhile = self._new_node(NodeType.ENDLOOP)
@ -128,9 +185,56 @@ class FunctionSolc(Function):
return node_endWhile
def _parse_for_compact_ast(self, statement, node):
body = statement['body']
init_expression = statement['initializationExpression']
condition = statement['condition']
loop_expression = statement['loopExpression']
node_startLoop = self._new_node(NodeType.STARTLOOP)
node_endLoop = self._new_node(NodeType.ENDLOOP)
if init_expression:
node_init_expression = self._parse_statement(init_expression, node)
link_nodes(node_init_expression, node_startLoop)
else:
link_nodes(node, node_startLoop)
if condition:
node_condition = self._new_node(NodeType.IFLOOP)
node_condition.add_unparsed_expression(condition)
link_nodes(node_startLoop, node_condition)
link_nodes(node_condition, node_endLoop)
else:
node_condition = node_startLoop
node_body = self._parse_statement(body, node_condition)
if loop_expression:
node_LoopExpression = self._parse_statement(loop_expression, node_body)
link_nodes(node_LoopExpression, node_startLoop)
else:
link_nodes(node_body, node_startLoop)
if not condition:
if not loop_expression:
# TODO: fix case where loop has no expression
link_nodes(node_startLoop, node_endLoop)
else:
link_nodes(node_LoopExpression, node_endLoop)
return node_endLoop
def _parse_for(self, statement, node):
# ForStatement = 'for' '(' (SimpleStatement)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement
# the handling of loop in the legacy ast is too complex
# to integrate the comapct ast
# its cleaner to do it separately
if self.is_compact_ast:
return self._parse_for_compact_ast(statement, node)
hasInitExession = True
hasCondition = True
hasLoopExpression = True
@ -153,11 +257,11 @@ class FunctionSolc(Function):
node_startLoop = self._new_node(NodeType.STARTLOOP)
node_endLoop = self._new_node(NodeType.ENDLOOP)
children = statement['children']
children = statement[self.get_children('children')]
if hasInitExession:
if len(children) >= 2:
if children[0]['name'] in ['VariableDefinitionStatement',
if children[0][self.get_key()] in ['VariableDefinitionStatement',
'VariableDeclarationStatement',
'ExpressionStatement']:
node_initExpression = self._parse_statement(children[0], node)
@ -176,7 +280,7 @@ class FunctionSolc(Function):
candidate = children[1]
else:
candidate = children[0]
if candidate['name'] not in ['VariableDefinitionStatement',
if candidate[self.get_key()] not in ['VariableDefinitionStatement',
'VariableDeclarationStatement',
'ExpressionStatement']:
node_condition = self._new_node(NodeType.IFLOOP)
@ -195,42 +299,45 @@ class FunctionSolc(Function):
node_LoopExpression = node_statement
if hasLoopExpression:
if len(children) > 2:
if children[-2]['name'] == 'ExpressionStatement':
if children[-2][self.get_key()] == 'ExpressionStatement':
node_LoopExpression = self._parse_statement(children[-2], node_statement)
if not hasCondition:
link_nodes(node_LoopExpression, node_endLoop)
if not hasCondition and not hasLoopExpression:
link_nodes(node, node_endLoop)
link_nodes(node_LoopExpression, node_startLoop)
return node_endLoop
def _parse_dowhile(self, doWhilestatement, node):
children = doWhilestatement['children']
node_startDoWhile = self._new_node(NodeType.STARTLOOP)
node_condition = self._new_node(NodeType.IFLOOP)
if self.is_compact_ast:
node_condition.add_unparsed_expression(doWhilestatement['condition'])
statement = self._parse_statement(doWhilestatement['body'], node_condition)
else:
children = doWhilestatement[self.get_children('children')]
# same order in the AST as while
node_condition = self._new_node(NodeType.IFLOOP)
#expression = parse_expression(children[0], self)
expression = children[0]
node_condition.add_unparsed_expression(expression)
statement = self._parse_statement(children[1], node_condition)
node_endDoWhile = self._new_node(NodeType.ENDLOOP)
link_nodes(node, node_startDoWhile)
link_nodes(node_startDoWhile, node_condition)
link_nodes(node_startDoWhile, statement)
link_nodes(statement, node_condition)
link_nodes(node_condition, node_endDoWhile)
return node_endDoWhile
def _parse_variable_definition(self, statement, node):
#assert len(statement['children']) == 1
# if there is, parse default value
#assert not 'attributes' in statement
try:
local_var = LocalVariableSolc(statement)
#local_var = LocalVariableSolc(statement['children'][0], statement['children'][1::])
local_var.set_function(self)
local_var.set_offset(statement['src'], self.contract.slither)
@ -243,10 +350,84 @@ class FunctionSolc(Function):
return new_node
except MultipleVariablesDeclaration:
# Custom handling of var (a,b) = .. style declaration
if self.is_compact_ast:
variables = statement['declarations']
count = len(variables)
if statement['initialValue']['nodeType'] == 'TupleExpression':
inits = statement['initialValue']['components']
i = 0
new_node = node
for variable in variables:
init = inits[i]
src = variable['src']
i = i+1
new_statement = {'nodeType':'VariableDefinitionStatement',
'src': src,
'declarations':[variable],
'initialValue':init}
new_node = self._parse_variable_definition(new_statement, new_node)
else:
# If we have
# var (a, b) = f()
# we can split in multiple declarations, without init
# Then we craft one expression that does the assignment
variables = []
i = 0
new_node = node
for variable in statement['declarations']:
i = i+1
if variable:
src = variable['src']
# Create a fake statement to be consistent
new_statement = {'nodeType':'VariableDefinitionStatement',
'src': src,
'declarations':[variable]}
variables.append(variable)
new_node = self._parse_variable_definition_init_tuple(new_statement,
i,
new_node)
var_identifiers = []
# craft of the expression doing the assignement
for v in variables:
identifier = {
'nodeType':'Identifier',
'src': v['src'],
'name': v['name'],
'typeDescriptions': {
'typeString':v['typeDescriptions']['typeString']
}
}
var_identifiers.append(identifier)
tuple_expression = {'nodeType':'TupleExpression',
'src': statement['src'],
'components':var_identifiers}
expression = {
'nodeType' : 'Assignment',
'src':statement['src'],
'operator': '=',
'type':'tuple()',
'leftHandSide': tuple_expression,
'rightHandSide': statement['initialValue'],
'typeDescriptions': {'typeString':'tuple()'}
}
node = new_node
new_node = self._new_node(NodeType.EXPRESSION)
new_node.add_unparsed_expression(expression)
link_nodes(node, new_node)
else:
count = 0
children = statement['children']
children = statement[self.get_children('children')]
child = children[0]
while child['name'] == 'VariableDeclaration':
while child[self.get_key()] == 'VariableDeclaration':
count = count +1
child = children[count]
@ -257,32 +438,32 @@ class FunctionSolc(Function):
variables_declaration = children[0:count]
i = 0
new_node = node
if tuple_vars['name'] == 'TupleExpression':
assert len(tuple_vars['children']) == count
if tuple_vars[self.get_key()] == 'TupleExpression':
assert len(tuple_vars[self.get_children('children')]) == count
for variable in variables_declaration:
init = tuple_vars['children'][i]
init = tuple_vars[self.get_children('children')][i]
src = variable['src']
i= i+1
i = i+1
# Create a fake statement to be consistent
new_statement = {'name':'VariableDefinitionStatement',
new_statement = {self.get_key():'VariableDefinitionStatement',
'src': src,
'children':[variable, init]}
self.get_children('children'):[variable, init]}
new_node = self._parse_variable_definition(new_statement, new_node)
else:
# If we have
# var (a, b) = f()
# we can split in multiple declarations, without init
# Then we craft one expression that does not assignment
assert tuple_vars['name'] in ['FunctionCall', 'Conditional']
# Then we craft one expression that does the assignment
assert tuple_vars[self.get_key()] in ['FunctionCall', 'Conditional']
variables = []
for variable in variables_declaration:
src = variable['src']
i= i+1
i = i+1
# Create a fake statement to be consistent
new_statement = {'name':'VariableDefinitionStatement',
new_statement = {self.get_key():'VariableDefinitionStatement',
'src': src,
'children':[variable]}
self.get_children('children'):[variable]}
variables.append(variable)
new_node = self._parse_variable_definition_init_tuple(new_statement, i, new_node)
@ -290,23 +471,23 @@ class FunctionSolc(Function):
# craft of the expression doing the assignement
for v in variables:
identifier = {
'name' : 'Identifier',
self.get_key() : 'Identifier',
'src': v['src'],
'attributes': {
'value': v['attributes']['name'],
'value': v['attributes'][self.get_key()],
'type': v['attributes']['type']}
}
var_identifiers.append(identifier)
expression = {
'name' : 'Assignment',
self.get_key() : 'Assignment',
'src':statement['src'],
'attributes': {'operator': '=',
'type':'tuple()'},
'children':
[{'name': 'TupleExpression',
self.get_children('children'):
[{self.get_key(): 'TupleExpression',
'src': statement['src'],
'children': var_identifiers},
self.get_children('children'): var_identifiers},
tuple_vars]}
node = new_node
new_node = self._new_node(NodeType.EXPRESSION)
@ -318,7 +499,7 @@ class FunctionSolc(Function):
def _parse_variable_definition_init_tuple(self, statement, index, node):
local_var = LocalVariableInitFromTupleSolc(statement, index)
#local_var = LocalVariableSolc(statement['children'][0], statement['children'][1::])
#local_var = LocalVariableSolc(statement[self.get_children('children')][0], statement[self.get_children('children')][1::])
local_var.set_function(self)
local_var.set_offset(statement['src'], self.contract.slither)
@ -342,7 +523,7 @@ class FunctionSolc(Function):
# Throw | EmitStatement | SimpleStatement ) ';'
# SimpleStatement = VariableDefinition | ExpressionStatement
name = statement['name']
name = statement[self.get_key()]
# SimpleStatement = VariableDefinition | ExpressionStatement
if name == 'IfStatement':
node = self._parse_if(statement, node)
@ -371,9 +552,13 @@ class FunctionSolc(Function):
elif name == 'Return':
return_node = self._new_node(NodeType.RETURN)
link_nodes(node, return_node)
if 'children' in statement and statement['children']:
assert len(statement['children']) == 1
expression = statement['children'][0]
if self.is_compact_ast:
if statement['expression']:
return_node.add_unparsed_expression(statement['expression'])
else:
if self.get_children('children') in statement and statement[self.get_children('children')]:
assert len(statement[self.get_children('children')]) == 1
expression = statement[self.get_children('children')][0]
return_node.add_unparsed_expression(expression)
node = return_node
elif name == 'Throw':
@ -381,8 +566,11 @@ class FunctionSolc(Function):
link_nodes(node, throw_node)
node = throw_node
elif name == 'EmitStatement':
#expression = parse_expression(statement['children'][0], self)
expression = statement['children'][0]
#expression = parse_expression(statement[self.get_children('children')][0], self)
if self.is_compact_ast:
expression = statement['eventCall']
else:
expression = statement[self.get_children('children')][0]
new_node = self._new_node(NodeType.EXPRESSION)
new_node.add_unparsed_expression(expression)
link_nodes(node, new_node)
@ -390,10 +578,13 @@ class FunctionSolc(Function):
elif name in ['VariableDefinitionStatement', 'VariableDeclarationStatement']:
node = self._parse_variable_definition(statement, node)
elif name == 'ExpressionStatement':
assert len(statement['children']) == 1
assert not 'attributes' in statement
#expression = parse_expression(statement['children'][0], self)
expression = statement['children'][0]
#assert len(statement[self.get_children('expression')]) == 1
#assert not 'attributes' in statement
#expression = parse_expression(statement[self.get_children('children')][0], self)
if self.is_compact_ast:
expression = statement[self.get_children('expression')]
else:
expression = statement[self.get_children('expression')][0]
new_node = self._new_node(NodeType.EXPRESSION)
new_node.add_unparsed_expression(expression)
link_nodes(node, new_node)
@ -409,20 +600,30 @@ class FunctionSolc(Function):
Return:
Node
'''
assert block['name'] == 'Block'
assert block[self.get_key()] == 'Block'
for child in block['children']:
node = self._parse_statement(child, node)
if self.is_compact_ast:
statements = block['statements']
else:
statements = block[self.get_children('children')]
for statement in statements:
node = self._parse_statement(statement, node)
return node
def _parse_cfg(self, cfg):
assert cfg['name'] == 'Block'
assert cfg[self.get_key()] == 'Block'
node = self._new_node(NodeType.ENTRYPOINT)
self._entry_point = node
if not cfg['children']:
if self.is_compact_ast:
statements = cfg['statements']
else:
statements = cfg[self.get_children('children')]
if not statements:
self._is_empty = True
else:
self._is_empty = False
@ -464,7 +665,7 @@ class FunctionSolc(Function):
end_node = self._find_end_loop(node, [])
if not end_node:
logger.error('Break in no-loop context {}'.format(node.nodeId()))
logger.error('Break in no-loop context {}'.format(node))
exit(-1)
for son in node.sons:
@ -527,10 +728,15 @@ class FunctionSolc(Function):
self._nodes = [n for n in self.nodes if not n in to_remove]
#
def _parse_params(self, params):
assert params[self.get_key()] == 'ParameterList'
assert params['name'] == 'ParameterList'
for param in params['children']:
assert param['name'] == 'VariableDeclaration'
if self.is_compact_ast:
params = params['parameters']
else:
params = params[self.get_children('children')]
for param in params:
assert param[self.get_key()] == 'VariableDeclaration'
local_var = LocalVariableSolc(param)
@ -547,9 +753,15 @@ class FunctionSolc(Function):
def _parse_returns(self, returns):
assert returns['name'] == 'ParameterList'
for ret in returns['children']:
assert ret['name'] == 'VariableDeclaration'
assert returns[self.get_key()] == 'ParameterList'
if self.is_compact_ast:
returns = returns['parameters']
else:
returns = returns[self.get_children('children')]
for ret in returns:
assert ret[self.get_key()] == 'VariableDeclaration'
local_var = LocalVariableSolc(ret)
@ -580,8 +792,11 @@ class FunctionSolc(Function):
self._analyze_attributes()
children = self._functionNotParsed['children']
if self.is_compact_ast:
params = self._functionNotParsed['parameters']
returns = self._functionNotParsed['returnParameters']
else:
children = self._functionNotParsed[self.get_children('children')]
params = children[0]
returns = children[1]
@ -596,15 +811,26 @@ class FunctionSolc(Function):
self._content_was_analyzed = True
children = self._functionNotParsed['children']
if self.is_compact_ast:
body = self._functionNotParsed['body']
if body and body[self.get_key()] == 'Block':
self._is_implemented = True
self._parse_cfg(body)
for modifier in self._functionNotParsed['modifiers']:
self._parse_modifier(modifier)
else:
children = self._functionNotParsed[self.get_children('children')]
self._is_implemented = False
for child in children[2:]:
if child['name'] == 'Block':
if child[self.get_key()] == 'Block':
self._is_implemented = True
self._parse_cfg(child)
continue
assert child['name'] == 'ModifierInvocation'
assert child[self.get_key()] == 'ModifierInvocation'
self._parse_modifier(child)
@ -630,13 +856,13 @@ class FunctionSolc(Function):
break
self._remove_alone_endif()
self._analyze_read_write()
self._analyze_calls()
def convert_expression_to_slithir(self):
for node in self.nodes:
node.slithir_generation()
transform_slithir_vars_to_ssa(self)
self._analyze_read_write()
self._analyze_calls()
def split_ternary_node(self, node, condition, true_expr, false_expr):

@ -19,8 +19,10 @@ class ModifierSolc(Modifier, FunctionSolc):
self._analyze_attributes()
if self.is_compact_ast:
params = self._functionNotParsed['parameters']
else:
children = self._functionNotParsed['children']
params = children[0]
if params:
@ -32,6 +34,15 @@ class ModifierSolc(Modifier, FunctionSolc):
self._content_was_analyzed = True
if self.is_compact_ast:
body = self._functionNotParsed['body']
if body and body[self.get_key()] == 'Block':
self._is_implemented = True
self._parse_cfg(body)
else:
children = self._functionNotParsed['children']
self._isImplemented = False
@ -39,7 +50,7 @@ class ModifierSolc(Modifier, FunctionSolc):
assert len(children) == 2
block = children[1]
assert block['name'] == 'Block'
self._isImplemented = False
self._isImplemented = True
self._parse_cfg(block)
for local_vars in self.variables:
@ -52,7 +63,7 @@ class ModifierSolc(Modifier, FunctionSolc):
self._analyze_calls()
def _parse_statement(self, statement, node):
name = statement['name']
name = statement[self.get_key()]
if name == 'PlaceholderStatement':
placeholder_node = self._new_node(NodeType.PLACEHOLDER)
link_nodes(node, placeholder_node)

@ -127,16 +127,30 @@ def find_variable(var_name, caller_context):
def parse_call(expression, caller_context):
attributes = expression['attributes']
if caller_context.is_compact_ast:
attributes = expression
type_conversion = expression['kind'] == 'typeConversion'
type_return = attributes['typeDescriptions']['typeString']
else:
attributes = expression['attributes']
type_conversion = attributes['type_conversion']
type_return = attributes['type']
children = expression['children']
if type_conversion:
assert len(children) == 2
type_call = parse_type(UnknownType(type_return), caller_context)
type_call = parse_type(UnknownType(attributes['type']), caller_context)
if caller_context.is_compact_ast:
type_info = expression['expression']
assert len(expression['arguments']) == 1
expression_to_parse = expression['arguments'][0]
else:
children = expression['children']
assert len(children) == 2
type_info = children[0]
expression_to_parse = children[1]
assert type_info['name'] in ['ElementaryTypenameExpression',
'ElementaryTypeNameExpression',
'Identifier',
@ -144,26 +158,36 @@ def parse_call(expression, caller_context):
'IndexAccess',
'MemberAccess']
expression = parse_expression(children[1], caller_context)
expression = parse_expression(expression_to_parse, caller_context)
t = TypeConversion(expression, type_call)
return t
assert children
type_call = attributes['type']
if caller_context.is_compact_ast:
called = parse_expression(expression['expression'], caller_context)
arguments = []
if expression['arguments']:
arguments = [parse_expression(a, caller_context) for a in expression['arguments']]
else:
children = expression['children']
called = parse_expression(children[0], caller_context)
arguments = [parse_expression(a, caller_context) for a in children[1::]]
if isinstance(called, SuperCallExpression):
return SuperCallExpression(called, arguments, type_call)
return CallExpression(called, arguments, type_call)
def parse_super_name(expression):
return SuperCallExpression(called, arguments, type_return)
return CallExpression(called, arguments, type_return)
def parse_super_name(expression, is_compact_ast):
if is_compact_ast:
assert expression['nodeType'] == 'MemberAccess'
attributes = expression
base_name = expression['memberName']
arguments = expression['typeDescriptions']['typeString']
else:
assert expression['name'] == 'MemberAccess'
attributes = expression['attributes']
base_name = attributes['member_name']
arguments = attributes['type']
assert arguments.startswith('function ')
# remove function (...()
arguments = arguments[len('function '):]
@ -237,22 +261,36 @@ def parse_expression(expression, caller_context):
# | PrimaryExpression
# The AST naming does not follow the spec
name = expression['name']
name = expression[caller_context.get_key()]
is_compact_ast = caller_context.is_compact_ast
if name == 'UnaryOperation':
if is_compact_ast:
attributes = expression
else:
attributes = expression['attributes']
assert 'prefix' in attributes
operation_type = UnaryOperationType.get_type(attributes['operator'], attributes['prefix'])
if is_compact_ast:
expression = parse_expression(expression['subExpression'], caller_context)
else:
assert len(expression['children']) == 1
expression = parse_expression(expression['children'][0], caller_context)
unary_op = UnaryOperation(expression, operation_type)
return unary_op
elif name == 'BinaryOperation':
if is_compact_ast:
attributes = expression
else:
attributes = expression['attributes']
operation_type = BinaryOperationType.get_type(attributes['operator'])
if is_compact_ast:
left_expression = parse_expression(expression['leftExpression'], caller_context)
right_expression = parse_expression(expression['rightExpression'], caller_context)
else:
assert len(expression['children']) == 2
left_expression = parse_expression(expression['children'][0], caller_context)
right_expression = parse_expression(expression['children'][1], caller_context)
@ -274,6 +312,9 @@ def parse_expression(expression, caller_context):
Note: this is only possible with Solidity >= 0.4.12
"""
if is_compact_ast:
expressions = [parse_expression(e, caller_context) if e else None for e in expression['components']]
else:
if 'children' not in expression :
attributes = expression['attributes']
components = attributes['components']
@ -294,22 +335,34 @@ def parse_expression(expression, caller_context):
return t
elif name == 'Conditional':
if is_compact_ast:
if_expression = parse_expression(expression['condition'], caller_context)
then_expression = parse_expression(expression['trueExpression'], caller_context)
else_expression = parse_expression(expression['falseExpression'], caller_context)
else:
children = expression['children']
assert len(children) == 3
if_expression = parse_expression(children[0], caller_context)
then_expression = parse_expression(children[1], caller_context)
else_expression = parse_expression(children[2], caller_context)
conditional = ConditionalExpression(if_expression, then_expression, else_expression)
#print(conditional)
return conditional
elif name == 'Assignment':
if is_compact_ast:
left_expression = parse_expression(expression['leftHandSide'], caller_context)
right_expression = parse_expression(expression['rightHandSide'], caller_context)
operation_type = AssignmentOperationType.get_type(expression['operator'])
operation_return_type = expression['typeDescriptions']['typeString']
else:
attributes = expression['attributes']
children = expression['children']
assert len(expression['children']) == 2
left_expression = parse_expression(children[0], caller_context)
right_expression = parse_expression(children[1], caller_context)
operation_type = AssignmentOperationType.get_type(attributes['operator'])
operation_return_type = attributes['type']
@ -318,6 +371,12 @@ def parse_expression(expression, caller_context):
elif name == 'Literal':
assert 'children' not in expression
if is_compact_ast:
value = expression['value']
if not value:
value = '0x'+expression['hexValue']
else:
value = expression['attributes']['value']
if value is None:
# for literal declared as hex
@ -329,9 +388,17 @@ def parse_expression(expression, caller_context):
elif name == 'Identifier':
assert 'children' not in expression
t = None
if caller_context.is_compact_ast:
value = expression['name']
t = expression['typeDescriptions']['typeString']
else:
value = expression['attributes']['value']
if 'type' in expression['attributes']:
t = expression['attributes']['type']
if t:
found = re.findall('[struct|enum|function|modifier] \(([\[\] ()a-zA-Z0-9\.,_]*)\)', t)
assert len(found) <= 1
@ -345,22 +412,34 @@ def parse_expression(expression, caller_context):
return identifier
elif name == 'IndexAccess':
if is_compact_ast:
index_type = expression['typeDescriptions']['typeString']
left = expression['baseExpression']
right = expression['indexExpression']
else:
index_type = expression['attributes']['type']
children = expression['children']
assert len(children) == 2
left_expression = parse_expression(children[0], caller_context)
right_expression = parse_expression(children[1], caller_context)
left = children[0]
right = children[1]
left_expression = parse_expression(left, caller_context)
right_expression = parse_expression(right, caller_context)
index = IndexAccess(left_expression, right_expression, index_type)
return index
elif name == 'MemberAccess':
if caller_context.is_compact_ast:
member_name = expression['memberName']
member_type = expression['typeDescriptions']['typeString']
member_expression = parse_expression(expression['expression'], caller_context)
else:
member_name = expression['attributes']['member_name']
member_type = expression['attributes']['type']
children = expression['children']
assert len(children) == 1
member_expression = parse_expression(children[0], caller_context)
if str(member_expression) == 'super':
super_name = parse_super_name(expression)
super_name = parse_super_name(expression, is_compact_ast)
if isinstance(caller_context, Contract):
inheritance = caller_context.inheritance
else:
@ -384,6 +463,9 @@ def parse_expression(expression, caller_context):
elif name == 'ElementaryTypeNameExpression':
# nop exression
# uint;
if is_compact_ast:
value = expression['typeName']
else:
assert 'children' not in expression
value = expression['attributes']['value']
t = parse_type(UnknownType(value), caller_context)
@ -393,45 +475,65 @@ def parse_expression(expression, caller_context):
# NewExpression is not a root expression, it's always the child of another expression
elif name == 'NewExpression':
new_type = expression['attributes']['type']
if is_compact_ast:
type_name = expression['typeName']
else:
children = expression['children']
assert len(children) == 1
#new_expression = parse_expression(children[0])
child = children[0]
type_name = children[0]
if child['name'] == 'ArrayTypeName':
if type_name[caller_context.get_key()] == 'ArrayTypeName':
depth = 0
while child['name'] == 'ArrayTypeName':
while type_name[caller_context.get_key()] == 'ArrayTypeName':
# Note: dont conserve the size of the array if provided
#assert len(child['children']) == 1
child = child['children'][0]
# We compute it directly
if is_compact_ast:
type_name = type_name['baseType']
else:
type_name = type_name['children'][0]
depth += 1
if child['name'] == 'ElementaryTypeName':
array_type = ElementaryType(child['attributes']['name'])
elif child['name'] == 'UserDefinedTypeName':
array_type = parse_type(UnknownType(child['attributes']['name']), caller_context)
if type_name[caller_context.get_key()] == 'ElementaryTypeName':
if is_compact_ast:
array_type = ElementaryType(type_name['name'])
else:
array_type = ElementaryType(type_name['attributes']['name'])
elif type_name[caller_context.get_key()] == 'UserDefinedTypeName':
if is_compact_ast:
array_type = parse_type(UnknownType(type_name['name']), caller_context)
else:
array_type = parse_type(UnknownType(type_name['attributes']['name']), caller_context)
else:
logger.error('Incorrect type array {}'.format(child))
logger.error('Incorrect type array {}'.format(type_name))
exit(-1)
array = NewArray(depth, array_type)
return array
if child['name'] == 'ElementaryTypeName':
elem_type = ElementaryType(child['attributes']['name'])
if type_name[caller_context.get_key()] == 'ElementaryTypeName':
if is_compact_ast:
elem_type = ElementaryType(type_name['name'])
else:
elem_type = ElementaryType(type_name['attributes']['name'])
new_elem = NewElementaryType(elem_type)
return new_elem
assert child['name'] == 'UserDefinedTypeName'
assert type_name[caller_context.get_key()] == 'UserDefinedTypeName'
contract_name = child['attributes']['name']
if is_compact_ast:
contract_name = type_name['name']
else:
contract_name = type_name['attributes']['name']
new = NewContract(contract_name)
return new
elif name == 'ModifierInvocation':
if is_compact_ast:
called = parse_expression(expression['modifierName'], caller_context)
arguments = []
if expression['arguments']:
arguments = [parse_expression(a, caller_context) for a in expression['arguments']]
else:
children = expression['children']
called = parse_expression(children[0], caller_context)
arguments = [parse_expression(a, caller_context) for a in children[1::]]

@ -18,7 +18,29 @@ class SlitherSolc(Slither):
self._contracts_by_id = {}
self._analyzed = False
self._is_compact_ast = False
def get_key(self):
if self._is_compact_ast:
return 'nodeType'
return 'name'
def get_children(self):
if self._is_compact_ast:
return 'nodes'
return 'children'
@property
def is_compact_ast(self):
return self._is_compact_ast
def _parse_contracts_from_json(self, json_data):
try:
data_loaded = json.loads(json_data)
self._parse_contracts_from_loaded_json(data_loaded['ast'], data_loaded['sourcePath'])
return True
except ValueError:
first = json_data.find('{')
if first != -1:
last = json_data.rfind('}') + 1
@ -26,50 +48,64 @@ class SlitherSolc(Slither):
json_data = json_data[first:last]
data_loaded = json.loads(json_data)
self._parse_contracts_from_loaded_json(data_loaded, filename)
return True
return False
def _parse_contracts_from_loaded_json(self, data_loaded, filename):
if 'nodeType' in data_loaded:
self._is_compact_ast = True
if data_loaded['name'] == 'root':
if data_loaded[self.get_key()] == 'root':
self._solc_version = '0.3'
logger.error('solc <0.4 is not supported')
return
elif data_loaded['name'] == 'SourceUnit':
elif data_loaded[self.get_key()] == 'SourceUnit':
self._solc_version = '0.4'
self._parse_source_unit(data_loaded, filename)
else:
logger.error('solc version is not supported')
return
for contract_data in data_loaded['children']:
for contract_data in data_loaded[self.get_children()]:
# if self.solc_version == '0.3':
# assert contract_data['name'] == 'Contract'
# assert contract_data[self.get_key()] == 'Contract'
# contract = ContractSolc03(self, contract_data)
if self.solc_version == '0.4':
assert contract_data['name'] in ['ContractDefinition', 'PragmaDirective', 'ImportDirective']
if contract_data['name'] == 'ContractDefinition':
assert contract_data[self.get_key()] in ['ContractDefinition', 'PragmaDirective', 'ImportDirective']
if contract_data[self.get_key()] == 'ContractDefinition':
contract = ContractSolc04(self, contract_data)
if 'src' in contract_data:
contract.set_offset(contract_data['src'], self)
self._contractsNotParsed.append(contract)
elif contract_data['name'] == 'PragmaDirective':
elif contract_data[self.get_key()] == 'PragmaDirective':
if self._is_compact_ast:
pragma = Pragma(contract_data['literals'])
else:
pragma = Pragma(contract_data['attributes']["literals"])
pragma.set_offset(contract_data['src'], self)
self._pragma_directives.append(pragma)
elif contract_data['name'] == 'ImportDirective':
elif contract_data[self.get_key()] == 'ImportDirective':
if self.is_compact_ast:
import_directive = Import(contract_data["absolutePath"])
else:
import_directive = Import(contract_data['attributes']["absolutePath"])
import_directive.set_offset(contract_data['src'], self)
self._import_directives.append(import_directive)
return True
return False
def _parse_source_unit(self, data, filename):
if data['name'] != 'SourceUnit':
if data[self.get_key()] != 'SourceUnit':
return -1 # handle solc prior 0.3.6
# match any char for filename
# filename can contain space, /, -, ..
name = re.findall('=* (.+) =*', filename)
if name:
assert len(name) == 1
name = name[0]
else:
name =filename
sourceUnit = -1 # handle old solc, or error
if 'src' in data:
@ -215,6 +251,9 @@ class SlitherSolc(Slither):
contract.set_is_analyzed(True)
def _analyze_struct_events(self, contract):
contract.analyze_constant_state_variables()
# Struct can refer to enum, or state variables
contract.analyze_structs()
# Event can refer to struct

@ -41,6 +41,8 @@ def _find_from_type_name(name, contract, contracts, structures, enums):
name_contract = name
if name_contract.startswith('contract '):
name_contract = name_contract[len('contract '):]
if name_contract.startswith('library '):
name_contract = name_contract[len('library '):]
var_type = next((c for c in contracts if c.name == name_contract), None)
if not var_type:
@ -131,6 +133,14 @@ def parse_type(t, caller_context):
logger.error('Incorrect caller context')
exit(-1)
is_compact_ast = caller_context.is_compact_ast
if is_compact_ast:
key = 'nodeType'
else:
key = 'name'
structures = contract.structures
enums = contract.enums
contracts = contract.slither.contracts
@ -138,14 +148,23 @@ def parse_type(t, caller_context):
if isinstance(t, UnknownType):
return _find_from_type_name(t.name, contract, contracts, structures, enums)
elif t['name'] == 'ElementaryTypeName':
return ElementaryType(t['attributes']['name'])
elif t[key] == 'ElementaryTypeName':
if is_compact_ast:
return ElementaryType(t['name'])
return ElementaryType(t['attributes'][key])
elif t['name'] == 'UserDefinedTypeName':
return _find_from_type_name(t['attributes']['name'], contract, contracts, structures, enums)
elif t[key] == 'UserDefinedTypeName':
if is_compact_ast:
return _find_from_type_name(t['typeDescriptions']['typeString'], contract, contracts, structures, enums)
return _find_from_type_name(t['attributes'][key], contract, contracts, structures, enums)
elif t['name'] == 'ArrayTypeName':
elif t[key] == 'ArrayTypeName':
length = None
if is_compact_ast:
if t['length']:
length = parse_expression(t['length'], caller_context)
array_type = parse_type(t['baseType'], contract)
else:
if len(t['children']) == 2:
length = parse_expression(t['children'][1], caller_context)
else:
@ -153,8 +172,12 @@ def parse_type(t, caller_context):
array_type = parse_type(t['children'][0], contract)
return ArrayType(array_type, length)
elif t['name'] == 'Mapping':
elif t[key] == 'Mapping':
if is_compact_ast:
mappingFrom = parse_type(t['keyType'], contract)
mappingTo = parse_type(t['valueType'], contract)
else:
assert len(t['children']) == 2
mappingFrom = parse_type(t['children'][0], contract)
@ -162,23 +185,29 @@ def parse_type(t, caller_context):
return MappingType(mappingFrom, mappingTo)
elif t['name'] == 'FunctionTypeName':
assert len(t['children']) == 2
elif t[key] == 'FunctionTypeName':
if is_compact_ast:
params = t['parameterTypes']
return_values = t['returnParameterTypes']
index = 'parameters'
else:
assert len(t['children']) == 2
params = t['children'][0]
return_values = t['children'][1]
index = 'children'
assert params['name'] == 'ParameterList'
assert return_values['name'] == 'ParameterList'
assert params[key] == 'ParameterList'
assert return_values[key] == 'ParameterList'
params_vars = []
return_values_vars = []
for p in params['children']:
for p in params[index]:
var = FunctionTypeVariableSolc(p)
var.set_offset(p['src'], caller_context.slither)
var.analyze(caller_context)
params_vars.append(var)
for p in return_values['children']:
for p in return_values[index]:
var = FunctionTypeVariableSolc(p)
var.set_offset(p['src'], caller_context.slither)

@ -33,7 +33,28 @@ class VariableDeclarationSolc(Variable):
self._elem_to_parse = None
self._initializedNotParsed = None
if var['name'] in ['VariableDeclarationStatement', 'VariableDefinitionStatement']:
self._is_compact_ast = False
if 'nodeType' in var:
self._is_compact_ast = True
nodeType = var['nodeType']
if nodeType in ['VariableDeclarationStatement', 'VariableDefinitionStatement']:
if len(var['declarations'])>1:
raise MultipleVariablesDeclaration
init = None
if 'initialValue' in var:
init = var['initialValue']
self._init_from_declaration(var['declarations'][0], init)
elif nodeType == 'VariableDeclaration':
self._init_from_declaration(var, var['value'])
else:
logger.error('Incorrect variable declaration type {}'.format(nodeType))
exit(-1)
else:
nodeType = var['name']
if nodeType in ['VariableDeclarationStatement', 'VariableDefinitionStatement']:
if len(var['children']) == 2:
init = var['children'][1]
elif len(var['children']) == 1:
@ -45,10 +66,10 @@ class VariableDeclarationSolc(Variable):
exit(-1)
declaration = var['children'][0]
self._init_from_declaration(declaration, init)
elif var['name'] == 'VariableDeclaration':
elif nodeType == 'VariableDeclaration':
self._init_from_declaration(var, None)
else:
logger.error('Incorrect variable declaration type {}'.format(var['name']))
logger.error('Incorrect variable declaration type {}'.format(nodeType))
exit(-1)
@property
@ -66,13 +87,17 @@ class VariableDeclarationSolc(Variable):
self._visibility = 'internal'
def _init_from_declaration(self, var, init):
if self._is_compact_ast:
attributes = var
self._typeName = attributes['typeDescriptions']['typeString']
else:
assert len(var['children']) <= 2
assert var['name'] == 'VariableDeclaration'
attributes = var['attributes']
self._name = attributes['name']
self._typeName = attributes['type']
self._name = attributes['name']
self._arrayDepth = 0
self._isMapping = False
self._mappingFrom = None
@ -85,6 +110,12 @@ class VariableDeclarationSolc(Variable):
self._analyze_variable_attributes(attributes)
if self._is_compact_ast:
if var['typeName']:
self._elem_to_parse = var['typeName']
else:
self._elem_to_parse = UnknownType(var['typeDescriptions']['typeString'])
else:
if not var['children']:
# It happens on variable declared inside loop declaration
try:
@ -95,6 +126,11 @@ class VariableDeclarationSolc(Variable):
else:
self._elem_to_parse = var['children'][0]
if self._is_compact_ast:
self._initializedNotParsed = init
if init:
self._initialized = True
else:
if init: # there are two way to init a var local in the AST
assert len(var['children']) <= 1
self._initialized = True

@ -28,6 +28,9 @@ def f_call(e, x):
def f_expression(e, x):
e._expression = x
def f_called(e, x):
e._called = x
class SplitTernaryExpression(object):
def __init__(self, expression):
@ -63,9 +66,18 @@ class SplitTernaryExpression(object):
if isinstance(expression, ConditionalExpression):
raise Exception('Nested ternary operator not handled')
if isinstance(expression, (Literal, Identifier, IndexAccess, MemberAccess)):
if isinstance(expression, (Literal, Identifier, IndexAccess)):
return None
# case of lib
# (.. ? .. : ..).add
if isinstance(expression, MemberAccess):
next_expr = expression.expression
if self.apply_copy(next_expr, true_expression, false_expression, f_expression):
self.copy_expression(next_expr,
true_expression.expression,
false_expression.expression)
elif isinstance(expression, (AssignmentOperation, BinaryOperation, TupleExpression)):
true_expression._expressions = []
false_expression._expressions = []
@ -73,12 +85,19 @@ class SplitTernaryExpression(object):
for next_expr in expression.expressions:
if self.apply_copy(next_expr, true_expression, false_expression, f_expressions):
# always on last arguments added
self.copy_expression(next_expr, true_expression.expressions[-1], false_expression.expressions[-1])
self.copy_expression(next_expr,
true_expression.expressions[-1],
false_expression.expressions[-1])
elif isinstance(expression, CallExpression):
next_expr = expression.called
true_expression._called = copy.copy(next_expr)
false_expression._called = copy.copy(next_expr)
# case of lib
# (.. ? .. : ..).add
if self.apply_copy(next_expr, true_expression, false_expression, f_called):
self.copy_expression(next_expr,
true_expression.called,
false_expression.called)
true_expression._arguments = []
false_expression._arguments = []
@ -86,12 +105,16 @@ class SplitTernaryExpression(object):
for next_expr in expression.arguments:
if self.apply_copy(next_expr, true_expression, false_expression, f_call):
# always on last arguments added
self.copy_expression(next_expr, true_expression.arguments[-1], false_expression.arguments[-1])
self.copy_expression(next_expr,
true_expression.arguments[-1],
false_expression.arguments[-1])
elif isinstance(expression, TypeConversion):
next_expr = expression.expression
if self.apply_copy(next_expr, true_expression, false_expression, f_expression):
self.copy_expression(expression.expression, true_expression.expression, false_expression.expression)
self.copy_expression(expression.expression,
true_expression.expression,
false_expression.expression)
else:
raise Exception('Ternary operation not handled {}'.format(type(expression)))

Loading…
Cancel
Save