diff --git a/slither/__main__.py b/slither/__main__.py index 9ccca453c..ec184d3ba 100644 --- a/slither/__main__.py +++ b/slither/__main__.py @@ -78,7 +78,10 @@ def process_truffle(dirname, args, detector_classes, printer_classes): package = json.load(f) if 'devDependencies' in package: if 'truffle' in package['devDependencies']: - truffle_version = 'truffle@{}'.format(package['devDependencies']['truffle']) + version = package['devDependencies']['truffle'] + if version.startswith('^'): + version = version[1:] + truffle_version = 'truffle@{}'.format(version) cmd = ['npx', truffle_version,'compile'] logger.info("'{}' running (use --truffle-version truffle@x.x.x to use specific version)".format(' '.join(cmd))) process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) diff --git a/slither/analyses/data_dependency/data_dependency.py b/slither/analyses/data_dependency/data_dependency.py index 95cd10754..94e3e534a 100644 --- a/slither/analyses/data_dependency/data_dependency.py +++ b/slither/analyses/data_dependency/data_dependency.py @@ -1,13 +1,14 @@ """ Compute the data depenency between all the SSA variables """ -from slither.core.declarations import Contract, Function -from slither.core.declarations.solidity_variables import \ - SolidityVariableComposed +from slither.core.declarations import (Contract, Enum, Function, + SolidityFunction, SolidityVariable, + SolidityVariableComposed, Structure) from slither.slithir.operations import Index, OperationWithLValue from slither.slithir.variables import (Constant, LocalIRVariable, - ReferenceVariable, StateIRVariable, - TemporaryVariable) + ReferenceVariable, ReferenceVariableSSA, + StateIRVariable, TemporaryVariable, + TemporaryVariableSSA, TupleVariableSSA) ################################################################################### ################################################################################### @@ -243,7 +244,6 @@ def compute_dependency_function(function): function.context[KEY_SSA_UNPROTECTED] = dict() is_protected = function.is_protected() - for node in function.nodes: for ir in node.irs_ssa: if isinstance(ir, OperationWithLValue) and ir.lvalue: @@ -259,10 +259,9 @@ def compute_dependency_function(function): function.context[KEY_NON_SSA_UNPROTECTED] = convert_to_non_ssa(function.context[KEY_SSA_UNPROTECTED]) def convert_variable_to_non_ssa(v): - if isinstance(v, (LocalIRVariable, StateIRVariable)): + if isinstance(v, (LocalIRVariable, StateIRVariable, TemporaryVariableSSA, ReferenceVariableSSA, TupleVariableSSA)): return v.non_ssa_version - if isinstance(v, (TemporaryVariable, ReferenceVariable)): - return next((variable for variable in v.function.slithir_variables if variable.name == v.name)) + assert isinstance(v, (Constant, SolidityVariable, Contract, Enum, SolidityFunction, Structure, Function)) return v def convert_to_non_ssa(data_depencies): diff --git a/slither/core/cfg/node.py b/slither/core/cfg/node.py index 7b15937e7..d4c70840e 100644 --- a/slither/core/cfg/node.py +++ b/slither/core/cfg/node.py @@ -10,12 +10,13 @@ from slither.core.source_mapping.source_mapping import SourceMapping from slither.core.variables.local_variable import LocalVariable from slither.core.variables.state_variable import StateVariable from slither.core.variables.variable import Variable +from slither.core.solidity_types import ElementaryType from slither.slithir.convert import convert_expression from slither.slithir.operations import (Balance, HighLevelCall, Index, InternalCall, Length, LibraryCall, LowLevelCall, Member, OperationWithLValue, Phi, PhiCallback, - SolidityCall) + SolidityCall, Return) from slither.slithir.variables import (Constant, LocalIRVariable, ReferenceVariable, StateIRVariable, TemporaryVariable, TupleVariable) @@ -424,11 +425,21 @@ class Node(SourceMapping, ChildFunction): def is_conditional(self, include_loop=True): """ Check if the node is a conditional node - A conditional node is either a IF or a require/assert + A conditional node is either a IF or a require/assert or a RETURN bool Returns: bool: True if the node is a conditional node """ - return self.contains_if(include_loop) or self.contains_require_or_assert() + if self.contains_if(include_loop) or self.contains_require_or_assert(): + return True + if self.irs: + last_ir = self.irs[-1] + if last_ir: + if isinstance(last_ir, Return): + for r in last_ir.read: + if r.type == ElementaryType('bool'): + return True + return False + # endregion @@ -685,6 +696,8 @@ class Node(SourceMapping, ChildFunction): 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)) + elif ir.destination == SolidityVariable('this'): + self._high_level_calls.append((self.function.contract, ir.function)) else: self._high_level_calls.append((ir.destination.type.type, ir.function)) elif isinstance(ir, LibraryCall): diff --git a/slither/detectors/reentrancy/reentrancy.py b/slither/detectors/reentrancy/reentrancy.py index 80b06c26b..527e80273 100644 --- a/slither/detectors/reentrancy/reentrancy.py +++ b/slither/detectors/reentrancy/reentrancy.py @@ -59,6 +59,8 @@ class Reentrancy(AbstractDetector): # We can check that the function called is # reentrancy-safe if ir.destination == SolidityVariable('this'): + if isinstance(ir.function, Variable): + continue if not ir.function.all_high_level_calls(): if not ir.function.all_low_level_calls(): continue diff --git a/slither/printers/call/call_graph.py b/slither/printers/call/call_graph.py index a68b7de49..ed5efdfae 100644 --- a/slither/printers/call/call_graph.py +++ b/slither/printers/call/call_graph.py @@ -142,6 +142,9 @@ class PrinterCallGraph(AbstractPrinter): Args: filename(string) """ + if not filename: + filename = "contracts.dot" + if not filename.endswith('.dot'): filename += '.dot' diff --git a/slither/printers/inheritance/inheritance_graph.py b/slither/printers/inheritance/inheritance_graph.py index 9897db4b2..468969002 100644 --- a/slither/printers/inheritance/inheritance_graph.py +++ b/slither/printers/inheritance/inheritance_graph.py @@ -171,7 +171,7 @@ class PrinterInheritanceGraph(AbstractPrinter): filename(string) """ if filename == '': - filename = 'export' + filename = 'contracts.dot' if not filename.endswith('.dot'): filename += ".dot" info = 'Inheritance Graph: ' + filename diff --git a/slither/slithir/utils/ssa.py b/slither/slithir/utils/ssa.py index 3b16555bb..d2e983616 100644 --- a/slither/slithir/utils/ssa.py +++ b/slither/slithir/utils/ssa.py @@ -1,6 +1,9 @@ import logging from slither.core.cfg.node import NodeType +from slither.core.declarations import (Contract, Enum, Function, + SolidityFunction, SolidityVariable, + SolidityVariableComposed, Structure) from slither.core.variables.local_variable import LocalVariable from slither.core.variables.state_variable import StateVariable from slither.slithir.operations import (Assignment, Balance, Binary, Condition, @@ -14,9 +17,10 @@ from slither.slithir.operations import (Assignment, Balance, Binary, Condition, Push, Return, Send, SolidityCall, Transfer, TypeConversion, Unary, Unpack) -from slither.slithir.variables import (LocalIRVariable, ReferenceVariable, +from slither.slithir.variables import (Constant, LocalIRVariable, + ReferenceVariable, ReferenceVariableSSA, StateIRVariable, TemporaryVariable, - TupleVariable) + TemporaryVariableSSA, TupleVariable, TupleVariableSSA) logger = logging.getLogger('SSA_Conversion') @@ -164,6 +168,7 @@ def generate_ssa_irs(node, local_variables_instances, all_local_variables_instan # They dont need phi function temporary_variables_instances = dict() reference_variables_instances = dict() + tuple_variables_instances = dict() for ir in node.irs: new_ir = copy_ir(ir, @@ -171,6 +176,7 @@ def generate_ssa_irs(node, local_variables_instances, all_local_variables_instan state_variables_instances, temporary_variables_instances, reference_variables_instances, + tuple_variables_instances, all_local_variables_instances) update_lvalue(new_ir, @@ -416,7 +422,81 @@ def add_phi_origins(node, local_variables_definition, state_variables_definition ################################################################################### ################################################################################### -def copy_ir(ir, local_variables_instances, state_variables_instances, temporary_variables_instances, reference_variables_instances, all_local_variables_instances): +def get(variable, local_variables_instances, state_variables_instances, temporary_variables_instances, reference_variables_instances, tuple_variables_instances, all_local_variables_instances): + # variable can be None + # for example, on LowLevelCall, ir.lvalue can be none + if variable is None: + return None + if isinstance(variable, LocalVariable): + if variable.name in local_variables_instances: + return local_variables_instances[variable.name] + new_var = LocalIRVariable(variable) + local_variables_instances[variable.name] = new_var + all_local_variables_instances[variable.name] = new_var + return new_var + if isinstance(variable, StateVariable) and variable.canonical_name in state_variables_instances: + return state_variables_instances[variable.canonical_name] + elif isinstance(variable, ReferenceVariable): + if not variable.index in reference_variables_instances: + new_variable = ReferenceVariableSSA(variable) + if variable.points_to: + new_variable.points_to = get(variable.points_to, + local_variables_instances, + state_variables_instances, + temporary_variables_instances, + reference_variables_instances, + tuple_variables_instances, + all_local_variables_instances) + new_variable.set_type(variable.type) + reference_variables_instances[variable.index] = new_variable + return reference_variables_instances[variable.index] + elif isinstance(variable, TemporaryVariable): + if not variable.index in temporary_variables_instances: + new_variable = TemporaryVariableSSA(variable) + new_variable.set_type(variable.type) + temporary_variables_instances[variable.index] = new_variable + return temporary_variables_instances[variable.index] + elif isinstance(variable, TupleVariable): + if not variable.index in tuple_variables_instances: + new_variable = TupleVariableSSA(variable) + new_variable.set_type(variable.type) + tuple_variables_instances[variable.index] = new_variable + return tuple_variables_instances[variable.index] + assert isinstance(variable, (Constant, + SolidityVariable, + Contract, + Enum, + SolidityFunction, + Structure, + Function)) + return variable + +def get_variable(ir, f, *instances): + variable = f(ir) + variable = get(variable, *instances) + return variable + +def _get_traversal(values, *instances): + ret = [] + for v in values: + if isinstance(v, list): + v = _get_traversal(v, *instances) + else: + v = get(v, *instances) + ret.append(v) + return ret + +def get_arguments(ir, *instances): + return _get_traversal(ir.arguments, *instances) + +def get_rec_values(ir, f, *instances): + # Use by InitArray and NewArray + # Potential recursive array(s) + ori_init_values = f(ir) + + return _get_traversal(ori_init_values, *instances) + +def copy_ir(ir, *instances): ''' Args: ir (Operation) @@ -427,213 +507,156 @@ def copy_ir(ir, local_variables_instances, state_variables_instances, temporary_ Note: temporary and reference can be indexed by int, as they dont need phi functions ''' - - def get(variable): - if isinstance(variable, LocalVariable): - if variable.name in local_variables_instances: - return local_variables_instances[variable.name] - new_var = LocalIRVariable(variable) - local_variables_instances[variable.name] = new_var - all_local_variables_instances[variable.name] = new_var - return new_var - if isinstance(variable, StateVariable) and variable.canonical_name in state_variables_instances: - return state_variables_instances[variable.canonical_name] - elif isinstance(variable, ReferenceVariable): - if not variable.index in reference_variables_instances: - new_variable = ReferenceVariable(variable.node, index=variable.index) - if variable.points_to: - new_variable.points_to = get(variable.points_to) - new_variable.set_type(variable.type) - reference_variables_instances[variable.index] = new_variable - return reference_variables_instances[variable.index] - elif isinstance(variable, TemporaryVariable): - if not variable.index in temporary_variables_instances: - new_variable = TemporaryVariable(variable.node, index=variable.index) - new_variable.set_type(variable.type) - temporary_variables_instances[variable.index] = new_variable - return temporary_variables_instances[variable.index] - return variable - - def get_variable(ir, f): - variable = f(ir) - variable = get(variable) - return variable - - def get_arguments(ir): - arguments = [] - for arg in ir.arguments: - arg = get(arg) - arguments.append(arg) - return arguments - - def get_rec_values(ir, f): - # Use by InitArray and NewArray - # Potential recursive array(s) - ori_init_values = f(ir) - - def traversal(values): - ret = [] - for v in values: - if isinstance(v, list): - v = traversal(v) - else: - v = get(v) - ret.append(v) - return ret - - return traversal(ori_init_values) - - if isinstance(ir, Assignment): - lvalue = get_variable(ir, lambda x: ir.lvalue) - rvalue = get_variable(ir, lambda x: ir.rvalue) + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) + rvalue = get_variable(ir, lambda x: x.rvalue, *instances) variable_return_type = ir.variable_return_type return Assignment(lvalue, rvalue, variable_return_type) elif isinstance(ir, Balance): - lvalue = get_variable(ir, lambda x: ir.lvalue) - value = get_variable(ir, lambda x: ir.value) + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) + value = get_variable(ir, lambda x: x.value, *instances) return Balance(value, lvalue) elif isinstance(ir, Binary): - lvalue = get_variable(ir, lambda x: ir.lvalue) - variable_left = get_variable(ir, lambda x: ir.variable_left) - variable_right = get_variable(ir, lambda x: ir.variable_right) + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) + variable_left = get_variable(ir, lambda x: x.variable_left, *instances) + variable_right = get_variable(ir, lambda x: x.variable_right, *instances) operation_type = ir.type return Binary(lvalue, variable_left, variable_right, operation_type) elif isinstance(ir, Condition): - val = get_variable(ir, lambda x: ir.value) + val = get_variable(ir, lambda x: x.value, *instances) return Condition(val) elif isinstance(ir, Delete): - lvalue = get_variable(ir, lambda x: ir.lvalue) - variable = get_variable(ir, lambda x: ir.variable) + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) + variable = get_variable(ir, lambda x: x.variable, *instances) return Delete(lvalue, variable) elif isinstance(ir, EventCall): name = ir.name return EventCall(name) elif isinstance(ir, HighLevelCall): # include LibraryCall - destination = get_variable(ir, lambda x: ir.destination) + destination = get_variable(ir, lambda x: x.destination, *instances) function_name = ir.function_name nbr_arguments = ir.nbr_arguments - lvalue = get_variable(ir, lambda x: ir.lvalue) + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) type_call = ir.type_call if isinstance(ir, LibraryCall): new_ir = LibraryCall(destination, function_name, nbr_arguments, lvalue, type_call) else: new_ir = HighLevelCall(destination, function_name, nbr_arguments, lvalue, type_call) new_ir.call_id = ir.call_id - new_ir.call_value = get_variable(ir, lambda x: ir.call_value) - new_ir.call_gas = get_variable(ir, lambda x: ir.call_gas) - new_ir.arguments = get_arguments(ir) + new_ir.call_value = get_variable(ir, lambda x: x.call_value, *instances) + new_ir.call_gas = get_variable(ir, lambda x: x.call_gas, *instances) + new_ir.arguments = get_arguments(ir, *instances) new_ir.function = ir.function return new_ir elif isinstance(ir, Index): - lvalue = get_variable(ir, lambda x: ir.lvalue) - variable_left = get_variable(ir, lambda x: ir.variable_left) - variable_right = get_variable(ir, lambda x: ir.variable_right) + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) + variable_left = get_variable(ir, lambda x: x.variable_left, *instances) + variable_right = get_variable(ir, lambda x: x.variable_right, *instances) index_type = ir.index_type return Index(lvalue, variable_left, variable_right, index_type) elif isinstance(ir, InitArray): - lvalue = get_variable(ir, lambda x: ir.lvalue) - init_values = get_rec_values(ir, lambda x: ir.init_values) + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) + init_values = get_rec_values(ir, lambda x: x.init_values, *instances) return InitArray(init_values, lvalue) elif isinstance(ir, InternalCall): function = ir.function nbr_arguments = ir.nbr_arguments - lvalue = get_variable(ir, lambda x: ir.lvalue) + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) type_call = ir.type_call new_ir = InternalCall(function, nbr_arguments, lvalue, type_call) - new_ir.arguments = get_arguments(ir) + new_ir.arguments = get_arguments(ir, *instances) return new_ir elif isinstance(ir, InternalDynamicCall): - lvalue = get_variable(ir, lambda x: ir.lvalue) - function = ir.function + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) + function = get_variable(ir, lambda x: x.function, *instances) function_type = ir.function_type new_ir = InternalDynamicCall(lvalue, function, function_type) - new_ir.arguments = get_arguments(ir) + new_ir.arguments = get_arguments(ir, *instances) return new_ir elif isinstance(ir, LowLevelCall): - destination = get_variable(ir, lambda x: x.destination) + destination = get_variable(ir, lambda x: x.destination, *instances) function_name = ir.function_name nbr_arguments = ir.nbr_arguments - lvalue = get_variable(ir, lambda x: ir.lvalue) + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) type_call = ir.type_call new_ir = LowLevelCall(destination, function_name, nbr_arguments, lvalue, type_call) new_ir.call_id = ir.call_id - new_ir.call_value = get_variable(ir, lambda x: ir.call_value) - new_ir.call_gas = get_variable(ir, lambda x: ir.call_gas) - new_ir.arguments = get_arguments(ir) + new_ir.call_value = get_variable(ir, lambda x: x.call_value, *instances) + new_ir.call_gas = get_variable(ir, lambda x: x.call_gas, *instances) + new_ir.arguments = get_arguments(ir, *instances) return new_ir elif isinstance(ir, Member): - lvalue = get_variable(ir, lambda x: ir.lvalue) - variable_left = get_variable(ir, lambda x: ir.variable_left) - variable_right = get_variable(ir, lambda x: ir.variable_right) + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) + variable_left = get_variable(ir, lambda x: x.variable_left, *instances) + variable_right = get_variable(ir, lambda x: x.variable_right, *instances) return Member(variable_left, variable_right, lvalue) elif isinstance(ir, NewArray): depth = ir.depth array_type = ir.array_type - lvalue = get_variable(ir, lambda x: ir.lvalue) + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) new_ir = NewArray(depth, array_type, lvalue) - new_ir.arguments = get_rec_values(ir, lambda x: ir.arguments) + new_ir.arguments = get_rec_values(ir, lambda x: x.arguments, *instances) return new_ir elif isinstance(ir, NewElementaryType): new_type = ir.type - lvalue = get_variable(ir, lambda x: ir.lvalue) + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) new_ir = NewElementaryType(new_type, lvalue) - new_ir.arguments = get_arguments(ir) + new_ir.arguments = get_arguments(ir, *instances) return new_ir elif isinstance(ir, NewContract): contract_name = ir.contract_name - lvalue = get_variable(ir, lambda x: ir.lvalue) + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) new_ir = NewContract(contract_name, lvalue) - new_ir.arguments = get_arguments(ir) + new_ir.arguments = get_arguments(ir, *instances) return new_ir elif isinstance(ir, NewStructure): structure = ir.structure - lvalue = get_variable(ir, lambda x: ir.lvalue) + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) new_ir = NewStructure(structure, lvalue) - new_ir.arguments = get_arguments(ir) + new_ir.arguments = get_arguments(ir, *instances) return new_ir elif isinstance(ir, Push): - array = get_variable(ir, lambda x: ir.array) - lvalue = get_variable(ir, lambda x: ir.lvalue) + array = get_variable(ir, lambda x: x.array, *instances) + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) return Push(array, lvalue) elif isinstance(ir, Return): - value = [get_variable(x, lambda y: y) for x in ir.values] - return Return(value) + values = get_rec_values(ir, lambda x: x.values, *instances) + return Return(values) elif isinstance(ir, Send): - destination = get_variable(ir, lambda x: ir.destination) - value = get_variable(ir, lambda x: ir.call_value) - lvalue = get_variable(ir, lambda x: ir.lvalue) + destination = get_variable(ir, lambda x: x.destination, *instances) + value = get_variable(ir, lambda x: x.call_value, *instances) + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) return Send(destination, value, lvalue) elif isinstance(ir, SolidityCall): function = ir.function nbr_arguments = ir.nbr_arguments - lvalue = get_variable(ir, lambda x: ir.lvalue) + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) type_call = ir.type_call new_ir = SolidityCall(function, nbr_arguments, lvalue, type_call) - new_ir.arguments = get_arguments(ir) + new_ir.arguments = get_arguments(ir, *instances) return new_ir elif isinstance(ir, Transfer): - destination = get_variable(ir, lambda x: ir.destination) - value = get_variable(ir, lambda x: ir.call_value) + destination = get_variable(ir, lambda x: x.destination, *instances) + value = get_variable(ir, lambda x: x.call_value, *instances) return Transfer(destination, value) elif isinstance(ir, TypeConversion): - lvalue = get_variable(ir, lambda x: ir.lvalue) - variable = get_variable(ir, lambda x: ir.variable) + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) + variable = get_variable(ir, lambda x: x.variable, *instances) variable_type = ir.type return TypeConversion(lvalue, variable, variable_type) elif isinstance(ir, Unary): - lvalue = get_variable(ir, lambda x: ir.lvalue) - rvalue = get_variable(ir, lambda x: ir.rvalue) + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) + rvalue = get_variable(ir, lambda x: x.rvalue, *instances) operation_type = ir.type return Unary(lvalue, rvalue, operation_type) elif isinstance(ir, Unpack): - lvalue = get_variable(ir, lambda x: ir.lvalue) - tuple_var = ir.tuple + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) + tuple_var = get_variable(ir, lambda x: x.tuple, *instances) idx = ir.index return Unpack(lvalue, tuple_var, idx) elif isinstance(ir, Length): - lvalue = get_variable(ir, lambda x: ir.lvalue) - value = get_variable(ir, lambda x: ir.value) + lvalue = get_variable(ir, lambda x: x.lvalue, *instances) + value = get_variable(ir, lambda x: x.value, *instances) return Length(value, lvalue) diff --git a/slither/slithir/variables/__init__.py b/slither/slithir/variables/__init__.py index 416d90108..624287318 100644 --- a/slither/slithir/variables/__init__.py +++ b/slither/slithir/variables/__init__.py @@ -1,6 +1,9 @@ from .constant import Constant from .reference import ReferenceVariable +from .reference_ssa import ReferenceVariableSSA from .temporary import TemporaryVariable +from .temporary_ssa import TemporaryVariableSSA from .tuple import TupleVariable +from .tuple_ssa import TupleVariableSSA from .local_variable import LocalIRVariable from .state_variable import StateIRVariable diff --git a/slither/slithir/variables/reference_ssa.py b/slither/slithir/variables/reference_ssa.py new file mode 100644 index 000000000..b8c10993c --- /dev/null +++ b/slither/slithir/variables/reference_ssa.py @@ -0,0 +1,18 @@ +''' + This class is used for the SSA version of slithIR + It is similar to the non-SSA version of slithIR + as the ReferenceVariable are in SSA form in both version +''' +from .reference import ReferenceVariable +from .variable import SlithIRVariable + +class ReferenceVariableSSA(ReferenceVariable): + + def __init__(self, reference): + super(ReferenceVariableSSA, self).__init__(reference.node, reference.index) + + self._non_ssa_version = reference + + @property + def non_ssa_version(self): + return self._non_ssa_version diff --git a/slither/slithir/variables/temporary_ssa.py b/slither/slithir/variables/temporary_ssa.py new file mode 100644 index 000000000..b36e73419 --- /dev/null +++ b/slither/slithir/variables/temporary_ssa.py @@ -0,0 +1,20 @@ +''' + This class is used for the SSA version of slithIR + It is similar to the non-SSA version of slithIR + as the TemporaryVariable are in SSA form in both version +''' +from .temporary import TemporaryVariable +from .variable import SlithIRVariable + +class TemporaryVariableSSA(TemporaryVariable): + + def __init__(self, temporary): + super(TemporaryVariableSSA, self).__init__(temporary.node, temporary.index) + + self._non_ssa_version = temporary + + + @property + def non_ssa_version(self): + return self._non_ssa_version + diff --git a/slither/slithir/variables/tuple.py b/slither/slithir/variables/tuple.py index d95c48e20..bfff48586 100644 --- a/slither/slithir/variables/tuple.py +++ b/slither/slithir/variables/tuple.py @@ -1,15 +1,21 @@ from .variable import SlithIRVariable from slither.core.variables.variable import Variable +from slither.core.children.child_node import ChildNode from slither.core.solidity_types.type import Type -class TupleVariable(SlithIRVariable): +class TupleVariable(ChildNode, SlithIRVariable): COUNTER = 0 - def __init__(self): + def __init__(self, node, index=None): super(TupleVariable, self).__init__() - self._index = TupleVariable.COUNTER - TupleVariable.COUNTER += 1 + if index is None: + self._index = TupleVariable.COUNTER + TupleVariable.COUNTER += 1 + else: + self._index = index + + self._node = node @property def index(self): diff --git a/slither/slithir/variables/tuple_ssa.py b/slither/slithir/variables/tuple_ssa.py new file mode 100644 index 000000000..04c050260 --- /dev/null +++ b/slither/slithir/variables/tuple_ssa.py @@ -0,0 +1,20 @@ +''' + This class is used for the SSA version of slithIR + It is similar to the non-SSA version of slithIR + as the TupleVariable are in SSA form in both version +''' +from .tuple import TupleVariable +from .variable import SlithIRVariable + +class TupleVariableSSA(TupleVariable): + + def __init__(self, t): + super(TupleVariableSSA, self).__init__(t.node, t.index) + + self._non_ssa_version = t + + + @property + def non_ssa_version(self): + return self._non_ssa_version + diff --git a/slither/solc_parsing/solidity_types/type_parsing.py b/slither/solc_parsing/solidity_types/type_parsing.py index 1a26612dd..2b5057dda 100644 --- a/slither/solc_parsing/solidity_types/type_parsing.py +++ b/slither/solc_parsing/solidity_types/type_parsing.py @@ -160,7 +160,10 @@ def parse_type(t, caller_context): 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) + + # Determine if we have a type node (otherwise we use the name node, as some older solc did not have 'type'). + type_name_key = 'type' if 'type' in t['attributes'] else key + return _find_from_type_name(t['attributes'][type_name_key], contract, contracts, structures, enums) elif t[key] == 'ArrayTypeName': length = None diff --git a/slither/visitors/slithir/expression_to_slithir.py b/slither/visitors/slithir/expression_to_slithir.py index fd191df56..a1f682f88 100644 --- a/slither/visitors/slithir/expression_to_slithir.py +++ b/slither/visitors/slithir/expression_to_slithir.py @@ -128,7 +128,7 @@ class ExpressionToSlithIR(ExpressionVisitor): # If tuple if expression.type_call.startswith('tuple(') and expression.type_call != 'tuple()': - val = TupleVariable() + val = TupleVariable(self._node) else: val = TemporaryVariable(self._node) internal_call = InternalCall(called, len(args), val, expression.type_call) @@ -139,7 +139,7 @@ class ExpressionToSlithIR(ExpressionVisitor): # If tuple if expression.type_call.startswith('tuple(') and expression.type_call != 'tuple()': - val = TupleVariable() + val = TupleVariable(self._node) else: val = TemporaryVariable(self._node)