From 22f8ed731d68db5ef38c698a53bb988b343271a2 Mon Sep 17 00:00:00 2001 From: Josselin Date: Wed, 3 Jul 2019 16:18:51 +0200 Subject: [PATCH 1/5] Create ModifierStatements to allow IR conversion on modifier call --- slither/core/cfg/node.py | 4 +++ slither/core/declarations/function.py | 21 +++++++++++++++- slither/printers/summary/slithir.py | 8 ++++++ slither/solc_parsing/declarations/function.py | 25 ++++++++++++++++--- 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/slither/core/cfg/node.py b/slither/core/cfg/node.py index 0ff442a2b..8a94a7caa 100644 --- a/slither/core/cfg/node.py +++ b/slither/core/cfg/node.py @@ -62,6 +62,10 @@ class NodeType: # Only modifier node PLACEHOLDER = 0x40 + # Node not related to the CFG + # Use for state variable declaration, or modifier calls + STANDALONE = 0x50 + # @staticmethod def str(t): diff --git a/slither/core/declarations/function.py b/slither/core/declarations/function.py index b5fa6712c..6025f7829 100644 --- a/slither/core/declarations/function.py +++ b/slither/core/declarations/function.py @@ -15,10 +15,13 @@ from slither.core.expressions import (Identifier, IndexAccess, MemberAccess, from slither.core.source_mapping.source_mapping import SourceMapping from slither.core.variables.state_variable import StateVariable + logger = logging.getLogger("Function") ReacheableNode = namedtuple('ReacheableNode', ['node', 'ir']) +ModifierStatements = namedtuple('Modifier', ['modifier', 'node']) + class Function(ChildContract, ChildInheritance, SourceMapping): """ Function class @@ -324,6 +327,13 @@ class Function(ChildContract, ChildInheritance, SourceMapping): """ list(Modifier): List of the modifiers """ + return [c.modifier for c in self._modifiers] + + @property + def modifiers_statements(self): + """ + list(ModifierCall): List of the modifiers call (include expression and irs) + """ return list(self._modifiers) @property @@ -335,7 +345,16 @@ class Function(ChildContract, ChildInheritance, SourceMapping): included. """ # This is a list of contracts internally, so we convert it to a list of constructor functions. - return [c.constructors_declared for c in self._explicit_base_constructor_calls if c.constructors_declared] + return [c.modifier.constructors_declared for c in self._explicit_base_constructor_calls if c.modifier.constructors_declared] + + @property + def explicit_base_constructor_calls_statements(self): + """ + list(ModifierCall): List of the base constructors called explicitly by this presumed constructor definition. + + """ + # This is a list of contracts internally, so we convert it to a list of constructor functions. + return [c for c in self._explicit_base_constructor_calls if c.modifier.constructors_declared] # endregion diff --git a/slither/printers/summary/slithir.py b/slither/printers/summary/slithir.py index cd4a7299f..c6e30073f 100644 --- a/slither/printers/summary/slithir.py +++ b/slither/printers/summary/slithir.py @@ -34,6 +34,14 @@ class PrinterSlithIR(AbstractPrinter): print('\t\tIRs:') for ir in node.irs: print('\t\t\t{}'.format(ir)) + for modifier_statement in function.modifiers_statements: + print(f'\t\tModifier Call {modifier_statement.node.expression}') + for ir in modifier_statement.node.irs: + print('\t\t\t{}'.format(ir)) + for modifier_statement in function.explicit_base_constructor_calls_statements: + print(f'\t\tConstructor Call {modifier_statement.node.expression}') + for ir in modifier_statement.node.irs: + print('\t\t\t{}'.format(ir)) for modifier in contract.modifiers: print('\tModifier {}'.format(modifier.canonical_name)) for node in modifier.nodes: diff --git a/slither/solc_parsing/declarations/function.py b/slither/solc_parsing/declarations/function.py index 721f49941..6da6233c5 100644 --- a/slither/solc_parsing/declarations/function.py +++ b/slither/solc_parsing/declarations/function.py @@ -4,7 +4,7 @@ import logging from slither.core.cfg.node import NodeType, link_nodes from slither.core.declarations.contract import Contract -from slither.core.declarations.function import Function +from slither.core.declarations.function import Function, ModifierStatements from slither.core.dominators.utils import (compute_dominance_frontier, compute_dominators) from slither.core.expressions import AssignmentOperation @@ -220,6 +220,12 @@ class FunctionSolc(Function): for node in self.nodes: node.analyze_expressions(self) + for modifier_statement in self.modifiers_statements: + modifier_statement.node.analyze_expressions(self) + + for modifier_statement in self.explicit_base_constructor_calls_statements: + modifier_statement.node.analyze_expressions(self) + self._filter_ternary() self._remove_alone_endif() @@ -897,9 +903,15 @@ class FunctionSolc(Function): self._expression_modifiers.append(m) for m in ExportValues(m).result(): if isinstance(m, Function): - self._modifiers.append(m) + node = self._new_node(NodeType.STANDALONE, modifier['src']) + node.add_unparsed_expression(modifier) + self._modifiers.append(ModifierStatements(modifier=m, + node=node)) elif isinstance(m, Contract): - self._explicit_base_constructor_calls.append(m) + node = self._new_node(NodeType.STANDALONE, modifier['src']) + node.add_unparsed_expression(modifier) + self._explicit_base_constructor_calls.append(ModifierStatements(modifier=m, + node=node)) # endregion ################################################################################### @@ -1122,6 +1134,13 @@ class FunctionSolc(Function): def generate_slithir_and_analyze(self): for node in self.nodes: node.slithir_generation() + + for modifier_statement in self.modifiers_statements: + modifier_statement.node.slithir_generation() + + for modifier_statement in self.explicit_base_constructor_calls_statements: + modifier_statement.node.slithir_generation() + self._analyze_read_write() self._analyze_calls() From 8da3b86a306a62300f6aa981a95045380924f620 Mon Sep 17 00:00:00 2001 From: Josselin Date: Fri, 5 Jul 2019 13:29:56 +0200 Subject: [PATCH 2/5] Add a new type of function: constructor_variable to hold state variable initialization Add Function.FunctionType to determine if the function is a constructor/fallback/constructor_variables API change: function.is_fallback() becomes a property --- slither/core/declarations/function.py | 209 ++++++++++++++++-- slither/solc_parsing/declarations/contract.py | 47 +++- slither/solc_parsing/declarations/function.py | 142 ++---------- slither/solc_parsing/slitherSolc.py | 4 + 4 files changed, 252 insertions(+), 150 deletions(-) diff --git a/slither/core/declarations/function.py b/slither/core/declarations/function.py index 6025f7829..631e262bf 100644 --- a/slither/core/declarations/function.py +++ b/slither/core/declarations/function.py @@ -4,6 +4,7 @@ import logging from collections import namedtuple from itertools import groupby +from enum import Enum from slither.core.children.child_contract import ChildContract from slither.core.children.child_inheritance import ChildInheritance @@ -13,8 +14,9 @@ from slither.core.declarations.solidity_variables import (SolidityFunction, from slither.core.expressions import (Identifier, IndexAccess, MemberAccess, UnaryOperation) from slither.core.source_mapping.source_mapping import SourceMapping -from slither.core.variables.state_variable import StateVariable +from slither.core.variables.state_variable import StateVariable +from slither.utils.utils import unroll logger = logging.getLogger("Function") @@ -22,6 +24,12 @@ ReacheableNode = namedtuple('ReacheableNode', ['node', 'ir']) ModifierStatements = namedtuple('Modifier', ['modifier', 'node']) +class FunctionType(Enum): + NORMAL = 0 + CONSTRUCTOR = 1 + FALLBACK = 2 + CONSTRUCTOR_VARIABLES = 3 # Fake function to hold variable declaration statements + class Function(ChildContract, ChildInheritance, SourceMapping): """ Function class @@ -34,7 +42,7 @@ class Function(ChildContract, ChildInheritance, SourceMapping): self._pure = None self._payable = None self._visibility = None - self._is_constructor = None + self._is_implemented = None self._is_empty = None self._entry_point = None @@ -94,6 +102,9 @@ class Function(ChildContract, ChildInheritance, SourceMapping): self._reachable_from_nodes = set() self._reachable_from_functions = set() + # Constructor, fallback, State variable constructor + self._function_type = None + self._is_constructor = None ################################################################################### ################################################################################### @@ -106,11 +117,12 @@ class Function(ChildContract, ChildInheritance, SourceMapping): """ str: function name """ - if self._name == '': - if self.is_constructor: - return 'constructor' - else: - return 'fallback' + if self._function_type == FunctionType.CONSTRUCTOR: + return 'constructor' + elif self._function_type == FunctionType.FALLBACK: + return 'fallback' + elif self._function_type == FunctionType.CONSTRUCTOR_VARIABLES: + return 'slither_constructor_variables' return self._name @property @@ -131,12 +143,6 @@ class Function(ChildContract, ChildInheritance, SourceMapping): name, parameters, _ = self.signature return self.contract_declarer.name + '.' + name + '(' + ','.join(parameters) + ')' - @property - def is_constructor(self): - """ - bool: True if the function is the constructor - """ - return self._is_constructor or self._name == self.contract_declarer.name @property def contains_assembly(self): @@ -154,6 +160,41 @@ class Function(ChildContract, ChildInheritance, SourceMapping): """ return self.contract_declarer == contract + # endregion + ################################################################################### + ################################################################################### + # region Type (FunctionType) + ################################################################################### + ################################################################################### + + def set_function_type(self, t): + assert isinstance(t, FunctionType) + self._function_type = t + + @property + def is_constructor(self): + """ + bool: True if the function is the constructor + """ + return self._function_type == FunctionType.CONSTRUCTOR + + @property + def is_constructor_variables(self): + """ + bool: True if the function is the constructor of the variables + Slither has a inbuilt function to hold the state variables initialization + """ + return self._function_type == FunctionType.CONSTRUCTOR_VARIABLES + + @property + def is_fallback(self): + """ + Determine if the function is the fallback function for the contract + Returns + (bool) + """ + return self._function_type == FunctionType.FALLBACK + # endregion ################################################################################### ################################################################################### @@ -182,6 +223,9 @@ class Function(ChildContract, ChildInheritance, SourceMapping): """ return self._visibility + def set_visibility(self, v): + self._visibility = v + @property def view(self): """ @@ -248,6 +292,11 @@ class Function(ChildContract, ChildInheritance, SourceMapping): """ return self._entry_point + def add_node(self, node): + if not self._entry_point: + self._entry_point = node + self._nodes.append(node) + # endregion ################################################################################### ################################################################################### @@ -1005,13 +1054,6 @@ class Function(ChildContract, ChildInheritance, SourceMapping): args_vars = self.all_solidity_variables_used_as_args() return SolidityVariableComposed('msg.sender') in conditional_vars + args_vars - def is_fallback(self): - """ - Determine if the function is the fallback function for the contract - Returns - (bool) - """ - return self._name == "" and not self.is_constructor # endregion ################################################################################### @@ -1119,6 +1161,133 @@ class Function(ChildContract, ChildInheritance, SourceMapping): # endregion + ################################################################################### + ################################################################################### + # region SlithIr and SSA + ################################################################################### + ################################################################################### + + def get_last_ssa_state_variables_instances(self): + from slither.slithir.variables import ReferenceVariable + from slither.slithir.operations import OperationWithLValue + from slither.core.cfg.node import NodeType + + if not self.is_implemented: + return dict() + + # node, values + to_explore = [(self._entry_point, dict())] + # node -> values + explored = dict() + # name -> instances + ret = dict() + + while to_explore: + node, values = to_explore[0] + to_explore = to_explore[1::] + + if node.type != NodeType.ENTRYPOINT: + for ir_ssa in node.irs_ssa: + if isinstance(ir_ssa, OperationWithLValue): + lvalue = ir_ssa.lvalue + if isinstance(lvalue, ReferenceVariable): + lvalue = lvalue.points_to_origin + if isinstance(lvalue, StateVariable): + values[lvalue.canonical_name] = {lvalue} + + # Check for fixpoint + if node in explored: + if values == explored[node]: + continue + for k, instances in values.items(): + if not k in explored[node]: + explored[node][k] = set() + explored[node][k] |= instances + values = explored[node] + else: + explored[node] = values + + # Return condition + if not node.sons and node.type != NodeType.THROW: + for name, instances in values.items(): + if name not in ret: + ret[name] = set() + ret[name] |= instances + + for son in node.sons: + to_explore.append((son, dict(values))) + + return ret + + @staticmethod + def _unchange_phi(ir): + from slither.slithir.operations import (Phi, PhiCallback) + if not isinstance(ir, (Phi, PhiCallback)) or len(ir.rvalues) > 1: + return False + if not ir.rvalues: + return True + return ir.rvalues[0] == ir.lvalue + + def fix_phi(self, last_state_variables_instances, initial_state_variables_instances): + from slither.slithir.operations import (InternalCall, PhiCallback) + from slither.slithir.variables import (Constant, StateIRVariable) + for node in self.nodes: + for ir in node.irs_ssa: + if node == self.entry_point: + if isinstance(ir.lvalue, StateIRVariable): + additional = [initial_state_variables_instances[ir.lvalue.canonical_name]] + additional += last_state_variables_instances[ir.lvalue.canonical_name] + ir.rvalues = list(set(additional + ir.rvalues)) + # function parameter + else: + # find index of the parameter + idx = self.parameters.index(ir.lvalue.non_ssa_version) + # find non ssa version of that index + additional = [n.ir.arguments[idx] for n in self.reachable_from_nodes] + additional = unroll(additional) + additional = [a for a in additional if not isinstance(a, Constant)] + ir.rvalues = list(set(additional + ir.rvalues)) + if isinstance(ir, PhiCallback): + callee_ir = ir.callee_ir + if isinstance(callee_ir, InternalCall): + last_ssa = callee_ir.function.get_last_ssa_state_variables_instances() + if ir.lvalue.canonical_name in last_ssa: + ir.rvalues = list(last_ssa[ir.lvalue.canonical_name]) + else: + ir.rvalues = [ir.lvalue] + else: + additional = last_state_variables_instances[ir.lvalue.canonical_name] + ir.rvalues = list(set(additional + ir.rvalues)) + + node.irs_ssa = [ir for ir in node.irs_ssa if not self._unchange_phi(ir)] + + def generate_slithir_and_analyze(self): + for node in self.nodes: + node.slithir_generation() + + for modifier_statement in self.modifiers_statements: + modifier_statement.node.slithir_generation() + + for modifier_statement in self.explicit_base_constructor_calls_statements: + modifier_statement.node.slithir_generation() + + self._analyze_read_write() + self._analyze_calls() + + def generate_slithir_ssa(self, all_ssa_state_variables_instances): + from slither.slithir.utils.ssa import add_ssa_ir, transform_slithir_vars_to_ssa + from slither.core.dominators.utils import (compute_dominance_frontier, + compute_dominators) + compute_dominators(self.nodes) + compute_dominance_frontier(self.nodes) + transform_slithir_vars_to_ssa(self) + add_ssa_ir(self, all_ssa_state_variables_instances) + + def update_read_write_using_ssa(self): + for node in self.nodes: + node.update_read_write_using_ssa() + self._analyze_read_write() + ################################################################################### ################################################################################### # region Built in definitions diff --git a/slither/solc_parsing/declarations/contract.py b/slither/solc_parsing/declarations/contract.py index d3a01c51f..f5fba278c 100644 --- a/slither/solc_parsing/declarations/contract.py +++ b/slither/solc_parsing/declarations/contract.py @@ -1,7 +1,10 @@ import logging from slither.core.declarations.contract import Contract +from slither.core.declarations.function import Function, FunctionType from slither.core.declarations.enum import Enum +from slither.core.cfg.node import Node, NodeType +from slither.core.expressions import AssignmentOperation, Identifier, AssignmentOperationType from slither.slithir.variables import StateIRVariable from slither.solc_parsing.declarations.event import EventSolc from slither.solc_parsing.declarations.function import FunctionSolc @@ -319,7 +322,6 @@ class ContractSolc04(Contract): :return: """ all_elements = {} - accessible_elements = {} for father in self.inheritance: for element in getter(father): @@ -368,6 +370,49 @@ class ContractSolc04(Contract): pass return + + def _create_node(self, func, counter, variable): + # Function uses to create node for state variable declaration statements + node = Node(NodeType.STANDALONE, counter) + node.set_offset(variable.source_mapping, self.slither) + node.set_function(func) + func.add_node(node) + print(variable.expression) + expression = AssignmentOperation(Identifier(variable), + variable.expression, + AssignmentOperationType.ASSIGN, + variable.type) + + node.add_expression(expression) + return node + + def add_constructor_variables(self): + if self.state_variables: + found_candidate = False + for (idx, variable_candidate) in enumerate(self.state_variables): + if variable_candidate.expression and not variable_candidate.is_constant: + found_candidate = True + break + if found_candidate: + constructor_variable = Function() + constructor_variable.set_function_type(FunctionType.CONSTRUCTOR_VARIABLES) + constructor_variable.set_contract(self) + constructor_variable.set_contract_declarer(self) + constructor_variable.set_visibility('internal') + self._functions[constructor_variable.canonical_name] = constructor_variable + + prev_node = self._create_node(constructor_variable, 0, variable_candidate) + counter = 1 + for v in self.state_variables[idx+1:]: + if v.expression and not v.is_constant: + next_node = self._create_node(constructor_variable, counter, v) + prev_node.add_son(next_node) + next_node.add_father(prev_node) + counter += 1 + + + + def analyze_state_variables(self): for var in self.variables: var.analyze(self) diff --git a/slither/solc_parsing/declarations/function.py b/slither/solc_parsing/declarations/function.py index 6da6233c5..5c95cc231 100644 --- a/slither/solc_parsing/declarations/function.py +++ b/slither/solc_parsing/declarations/function.py @@ -4,16 +4,10 @@ import logging from slither.core.cfg.node import NodeType, link_nodes from slither.core.declarations.contract import Contract -from slither.core.declarations.function import Function, ModifierStatements -from slither.core.dominators.utils import (compute_dominance_frontier, - compute_dominators) +from slither.core.declarations.function import Function, ModifierStatements, FunctionType + from slither.core.expressions import AssignmentOperation -from slither.core.variables.state_variable import StateVariable -from slither.slithir.operations import (InternalCall, OperationWithLValue, Phi, - PhiCallback) -from slither.slithir.utils.ssa import add_ssa_ir, transform_slithir_vars_to_ssa -from slither.slithir.variables import (Constant, ReferenceVariable, - StateIRVariable) + from slither.solc_parsing.cfg.node import NodeSolc from slither.solc_parsing.expressions.expression_parsing import \ parse_expression @@ -23,7 +17,6 @@ from slither.solc_parsing.variables.local_variable_init_from_tuple import \ from slither.solc_parsing.variables.variable_declaration import \ MultipleVariablesDeclaration from slither.utils.expression_manipulations import SplitTernaryExpression -from slither.utils.utils import unroll from slither.visitors.expression.export_values import ExportValues from slither.visitors.expression.has_conditional import HasConditional from slither.solc_parsing.exceptions import ParsingError @@ -139,14 +132,20 @@ class FunctionSolc(Function): if 'constant' in attributes: self._view = attributes['constant'] - self._is_constructor = False + if self._name == '': + self._function_type = FunctionType.FALLBACK + else: + self._function_type = FunctionType.NORMAL + + if self._name == self.contract_declarer.name: + self._function_type = FunctionType.CONSTRUCTOR - if 'isConstructor' in attributes: - self._is_constructor = attributes['isConstructor'] + if 'isConstructor' in attributes and attributes['isConstructor']: + self._function_type = FunctionType.CONSTRUCTOR if 'kind' in attributes: if attributes['kind'] == 'constructor': - self._is_constructor = True + self._function_type = FunctionType.CONSTRUCTOR if 'visibility' in attributes: self._visibility = attributes['visibility'] @@ -1038,120 +1037,5 @@ class FunctionSolc(Function): # endregion - ################################################################################### - ################################################################################### - # region SlithIr and SSA - ################################################################################### - ################################################################################### - - def get_last_ssa_state_variables_instances(self): - if not self.is_implemented: - return dict() - - # node, values - to_explore = [(self._entry_point, dict())] - # node -> values - explored = dict() - # name -> instances - ret = dict() - - while to_explore: - node, values = to_explore[0] - to_explore = to_explore[1::] - - if node.type != NodeType.ENTRYPOINT: - for ir_ssa in node.irs_ssa: - if isinstance(ir_ssa, OperationWithLValue): - lvalue = ir_ssa.lvalue - if isinstance(lvalue, ReferenceVariable): - lvalue = lvalue.points_to_origin - if isinstance(lvalue, StateVariable): - values[lvalue.canonical_name] = {lvalue} - - # Check for fixpoint - if node in explored: - if values == explored[node]: - continue - for k, instances in values.items(): - if not k in explored[node]: - explored[node][k] = set() - explored[node][k] |= instances - values = explored[node] - else: - explored[node] = values - - # Return condition - if not node.sons and node.type != NodeType.THROW: - for name, instances in values.items(): - if name not in ret: - ret[name] = set() - ret[name] |= instances - - for son in node.sons: - to_explore.append((son, dict(values))) - return ret - - @staticmethod - def _unchange_phi(ir): - if not isinstance(ir, (Phi, PhiCallback)) or len(ir.rvalues) > 1: - return False - if not ir.rvalues: - return True - return ir.rvalues[0] == ir.lvalue - - def fix_phi(self, last_state_variables_instances, initial_state_variables_instances): - for node in self.nodes: - for ir in node.irs_ssa: - if node == self.entry_point: - if isinstance(ir.lvalue, StateIRVariable): - additional = [initial_state_variables_instances[ir.lvalue.canonical_name]] - additional += last_state_variables_instances[ir.lvalue.canonical_name] - ir.rvalues = list(set(additional + ir.rvalues)) - # function parameter - else: - # find index of the parameter - idx = self.parameters.index(ir.lvalue.non_ssa_version) - # find non ssa version of that index - additional = [n.ir.arguments[idx] for n in self.reachable_from_nodes] - additional = unroll(additional) - additional = [a for a in additional if not isinstance(a, Constant)] - ir.rvalues = list(set(additional + ir.rvalues)) - if isinstance(ir, PhiCallback): - callee_ir = ir.callee_ir - if isinstance(callee_ir, InternalCall): - last_ssa = callee_ir.function.get_last_ssa_state_variables_instances() - if ir.lvalue.canonical_name in last_ssa: - ir.rvalues = list(last_ssa[ir.lvalue.canonical_name]) - else: - ir.rvalues = [ir.lvalue] - else: - additional = last_state_variables_instances[ir.lvalue.canonical_name] - ir.rvalues = list(set(additional + ir.rvalues)) - - node.irs_ssa = [ir for ir in node.irs_ssa if not self._unchange_phi(ir)] - - def generate_slithir_and_analyze(self): - for node in self.nodes: - node.slithir_generation() - - for modifier_statement in self.modifiers_statements: - modifier_statement.node.slithir_generation() - - for modifier_statement in self.explicit_base_constructor_calls_statements: - modifier_statement.node.slithir_generation() - - self._analyze_read_write() - self._analyze_calls() - - def generate_slithir_ssa(self, all_ssa_state_variables_instances): - compute_dominators(self.nodes) - compute_dominance_frontier(self.nodes) - transform_slithir_vars_to_ssa(self) - add_ssa_ir(self, all_ssa_state_variables_instances) - - def update_read_write_using_ssa(self): - for node in self.nodes: - node.update_read_write_using_ssa() - self._analyze_read_write() diff --git a/slither/solc_parsing/slitherSolc.py b/slither/solc_parsing/slitherSolc.py index 6d11e2cc8..e6ac1c932 100644 --- a/slither/solc_parsing/slitherSolc.py +++ b/slither/solc_parsing/slitherSolc.py @@ -373,10 +373,14 @@ class SlitherSolc(Slither): contract.analyze_content_modifiers() contract.analyze_content_functions() + + contract.set_is_analyzed(True) def _convert_to_slithir(self): + for contract in self.contracts: + contract.add_constructor_variables() contract.convert_expression_to_slithir() self._propagate_function_calls() for contract in self.contracts: From 5b636f5b4312afb467d98d1f6a135cf8e3376b9b Mon Sep 17 00:00:00 2001 From: Josselin Date: Fri, 5 Jul 2019 13:41:16 +0200 Subject: [PATCH 3/5] Improve constructor call support --- slither/slithir/convert.py | 6 ++++++ slither/slithir/tmp_operations/tmp_call.py | 9 ++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/slither/slithir/convert.py b/slither/slithir/convert.py index 63284db77..101604d55 100644 --- a/slither/slithir/convert.py +++ b/slither/slithir/convert.py @@ -586,6 +586,12 @@ def extract_tmp_call(ins, contract): if isinstance(ins.called, Event): return EventCall(ins.called.name) + if isinstance(ins.called, Contract): + internalcall = InternalCall(ins.called.constructor, ins.nbr_arguments, ins.lvalue, + ins.type_call) + internalcall.call_id = ins.call_id + return internalcall + raise Exception('Not extracted {} {}'.format(type(ins.called), ins)) diff --git a/slither/slithir/tmp_operations/tmp_call.py b/slither/slithir/tmp_operations/tmp_call.py index 0d7fda1e9..3c8faec3c 100644 --- a/slither/slithir/tmp_operations/tmp_call.py +++ b/slither/slithir/tmp_operations/tmp_call.py @@ -1,14 +1,13 @@ -from slither.slithir.operations.lvalue import OperationWithLValue +from slither.core.declarations import Event, Contract, SolidityVariableComposed, SolidityFunction, Structure from slither.core.variables.variable import Variable -from slither.core.declarations.solidity_variables import SolidityVariableComposed, SolidityFunction -from slither.core.declarations.structure import Structure -from slither.core.declarations.event import Event +from slither.slithir.operations.lvalue import OperationWithLValue class TmpCall(OperationWithLValue): def __init__(self, called, nbr_arguments, result, type_call): - assert isinstance(called, (Variable, + assert isinstance(called, (Contract, + Variable, SolidityVariableComposed, SolidityFunction, Structure, From ce4485317795f8361c8a3975d5ed5ba1bdcbfd0c Mon Sep 17 00:00:00 2001 From: Josselin Date: Fri, 5 Jul 2019 15:25:14 +0200 Subject: [PATCH 4/5] Minor fixes Fix travis --- scripts/tests_generate_expected_json_4.sh | 2 +- slither/core/declarations/function.py | 2 +- .../possible_const_state_variables.py | 2 +- slither/solc_parsing/declarations/contract.py | 1 - ...deprecated_calls.deprecated-standards.json | 80 +++++++++++++++++++ .../deprecated_calls.deprecated-standards.txt | 4 +- utils/similarity/encode.py | 2 +- 7 files changed, 87 insertions(+), 6 deletions(-) diff --git a/scripts/tests_generate_expected_json_4.sh b/scripts/tests_generate_expected_json_4.sh index 4e1def80c..e748e4f42 100755 --- a/scripts/tests_generate_expected_json_4.sh +++ b/scripts/tests_generate_expected_json_4.sh @@ -21,7 +21,7 @@ generate_expected_json(){ } -#generate_expected_json tests/deprecated_calls.sol "deprecated-standards" +generate_expected_json tests/deprecated_calls.sol "deprecated-standards" #generate_expected_json tests/erc20_indexed.sol "erc20-indexed" #generate_expected_json tests/incorrect_erc20_interface.sol "erc20-interface" #generate_expected_json tests/incorrect_erc721_interface.sol "erc721-interface" diff --git a/slither/core/declarations/function.py b/slither/core/declarations/function.py index 631e262bf..7243a7c03 100644 --- a/slither/core/declarations/function.py +++ b/slither/core/declarations/function.py @@ -122,7 +122,7 @@ class Function(ChildContract, ChildInheritance, SourceMapping): elif self._function_type == FunctionType.FALLBACK: return 'fallback' elif self._function_type == FunctionType.CONSTRUCTOR_VARIABLES: - return 'slither_constructor_variables' + return 'slitherConstructorVariables' return self._name @property diff --git a/slither/detectors/variables/possible_const_state_variables.py b/slither/detectors/variables/possible_const_state_variables.py index f062a0c7a..9d30bcfcf 100644 --- a/slither/detectors/variables/possible_const_state_variables.py +++ b/slither/detectors/variables/possible_const_state_variables.py @@ -76,7 +76,7 @@ class ConstCandidateStateVars(AbstractDetector): all_functions = [c.all_functions_called for c in self.slither.contracts] all_functions = list(set([item for sublist in all_functions for item in sublist])) - all_variables_written = [f.state_variables_written for f in all_functions] + all_variables_written = [f.state_variables_written for f in all_functions if not f.is_constructor_variables] all_variables_written = set([item for sublist in all_variables_written for item in sublist]) constable_variables = [v for v in all_non_constant_elementary_variables diff --git a/slither/solc_parsing/declarations/contract.py b/slither/solc_parsing/declarations/contract.py index f5fba278c..e931dddb7 100644 --- a/slither/solc_parsing/declarations/contract.py +++ b/slither/solc_parsing/declarations/contract.py @@ -377,7 +377,6 @@ class ContractSolc04(Contract): node.set_offset(variable.source_mapping, self.slither) node.set_function(func) func.add_node(node) - print(variable.expression) expression = AssignmentOperation(Identifier(variable), variable.expression, AssignmentOperationType.ASSIGN, diff --git a/tests/expected_json/deprecated_calls.deprecated-standards.json b/tests/expected_json/deprecated_calls.deprecated-standards.json index c25d611b8..2b9c385d1 100644 --- a/tests/expected_json/deprecated_calls.deprecated-standards.json +++ b/tests/expected_json/deprecated_calls.deprecated-standards.json @@ -692,6 +692,86 @@ } } ] + }, + { + "check": "deprecated-standards", + "impact": "Informational", + "confidence": "High", + "description": "Deprecated standard detected @ tests/deprecated_calls.sol#2:\n\t- Usage of \"block.blockhash()\" should be replaced with \"blockhash()\"\n", + "elements": [ + { + "type": "node", + "name": "globalBlockHash = block.blockhash(0)", + "source_mapping": { + "start": 48, + "length": 44, + "filename_used": "/home/travis/build/crytic/slither/tests/deprecated_calls.sol", + "filename_relative": "tests/deprecated_calls.sol", + "filename_absolute": "/home/travis/build/crytic/slither/tests/deprecated_calls.sol", + "filename_short": "tests/deprecated_calls.sol", + "is_dependency": false, + "lines": [ + 2 + ], + "starting_column": 5, + "ending_column": 49 + }, + "type_specific_fields": { + "parent": { + "type": "function", + "name": "slitherConstructorVariables", + "source_mapping": null, + "type_specific_fields": { + "parent": { + "type": "contract", + "name": "ContractWithDeprecatedReferences", + "source_mapping": { + "start": 0, + "length": 906, + "filename_used": "/home/travis/build/crytic/slither/tests/deprecated_calls.sol", + "filename_relative": "tests/deprecated_calls.sol", + "filename_absolute": "/home/travis/build/crytic/slither/tests/deprecated_calls.sol", + "filename_short": "tests/deprecated_calls.sol", + "is_dependency": false, + "lines": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27 + ], + "starting_column": 1, + "ending_column": null + } + }, + "signature": "slitherConstructorVariables()" + } + } + } + } + ] } ] } diff --git a/tests/expected_json/deprecated_calls.deprecated-standards.txt b/tests/expected_json/deprecated_calls.deprecated-standards.txt index 7056ea4ce..63d07a08d 100644 --- a/tests/expected_json/deprecated_calls.deprecated-standards.txt +++ b/tests/expected_json/deprecated_calls.deprecated-standards.txt @@ -13,5 +13,7 @@ Deprecated standard detected @ tests/deprecated_calls.sol#22: - Usage of "callcode" should be replaced with "delegatecall" Deprecated standard detected @ tests/deprecated_calls.sol#25: - Usage of "suicide()" should be replaced with "selfdestruct()" +Deprecated standard detected @ tests/deprecated_calls.sol#2: + - Usage of "block.blockhash()" should be replaced with "blockhash()" Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#deprecated-standards -INFO:Slither:tests/deprecated_calls.sol analyzed (1 contracts), 7 result(s) found +INFO:Slither:tests/deprecated_calls.sol analyzed (1 contracts), 8 result(s) found diff --git a/utils/similarity/encode.py b/utils/similarity/encode.py index f20d316a8..06b3691ed 100644 --- a/utils/similarity/encode.py +++ b/utils/similarity/encode.py @@ -195,7 +195,7 @@ def encode_contract(cfilename, **kwargs): # Iterate over all the functions for function in contract.functions_declared: - if function.nodes == []: + if function.nodes == [] or function.is_constructor_variables: continue x = (cfilename,contract.name,function.name) From 3fff0bf5b286cdf5d3802fdf7439f26f972a9356 Mon Sep 17 00:00:00 2001 From: Josselin Date: Fri, 5 Jul 2019 15:34:59 +0200 Subject: [PATCH 5/5] Upadte etherscan travis test --- scripts/travis_test_etherscan.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/travis_test_etherscan.sh b/scripts/travis_test_etherscan.sh index c96eca12e..68b03f89f 100755 --- a/scripts/travis_test_etherscan.sh +++ b/scripts/travis_test_etherscan.sh @@ -18,7 +18,7 @@ fi slither rinkeby:0xFe05820C5A92D9bc906D4A46F662dbeba794d3b7 --solc "./solc-0.4.25" -if [ $? -ne 76 ] +if [ $? -ne 75 ] then echo "Etherscan test failed" exit -1