API changes:

- use slihtIR for node.state_vars read/write and calls
 - remove external_calls of node/function, split into node.low_level_calls/high_level_calls
pull/58/head
Josselin 6 years ago
parent 5b877ddceb
commit 5c1e405969
  1. 102
      slither/core/cfg/node.py
  2. 71
      slither/core/declarations/function.py
  3. 10
      slither/slithir/convert.py
  4. 26
      slither/solc_parsing/cfg/node.py
  5. 4
      slither/solc_parsing/declarations/function.py

@ -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,40 @@ 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)): List of high level calls (external calls). 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 +261,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 +360,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,39 @@ 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)): List of high level calls (external calls). 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 +381,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 +393,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 +614,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):
"""

@ -544,8 +544,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 +568,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

@ -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)]

@ -630,13 +630,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):

Loading…
Cancel
Save