def convert_expression_to_slithir(self): for func in self.functions + self.modifiers: try: func.generate_slithir_and_analyze() except AttributeError: # This can happens for example if there is a call to an interface # And the interface is redefined due to contract's name reuse # But the available version misses some functions self.log_incorrect_parsing( f'Impossible to generate IR for {self.name}.{func.name}') all_ssa_state_variables_instances = dict() for contract in self.inheritance: for v in contract.state_variables_declared: new_var = StateIRVariable(v) all_ssa_state_variables_instances[v.canonical_name] = new_var self._initial_state_variables.append(new_var) for v in self.variables: if v.contract == self: new_var = StateIRVariable(v) all_ssa_state_variables_instances[v.canonical_name] = new_var self._initial_state_variables.append(new_var) for func in self.functions + self.modifiers: func.generate_slithir_ssa(all_ssa_state_variables_instances)
def update_lvalue(new_ir, node, local_variables_instances, all_local_variables_instances, state_variables_instances, all_state_variables_instances): if isinstance(new_ir, OperationWithLValue): lvalue = new_ir.lvalue update_through_ref = False if isinstance(new_ir, (Assignment, Binary)): if isinstance(lvalue, ReferenceVariable): update_through_ref = True while isinstance(lvalue, ReferenceVariable): lvalue = lvalue.points_to if isinstance(lvalue, (LocalIRVariable, StateIRVariable)): if isinstance(lvalue, LocalIRVariable): new_var = LocalIRVariable(lvalue) new_var.index = all_local_variables_instances[ lvalue.name].index + 1 all_local_variables_instances[lvalue.name] = new_var local_variables_instances[lvalue.name] = new_var else: new_var = StateIRVariable(lvalue) new_var.index = all_state_variables_instances[ lvalue.canonical_name].index + 1 all_state_variables_instances[lvalue.canonical_name] = new_var state_variables_instances[lvalue.canonical_name] = new_var if update_through_ref: phi_operation = Phi(new_var, {node}) phi_operation.rvalues = [lvalue] node.add_ssa_ir(phi_operation) if not isinstance(new_ir.lvalue, ReferenceVariable): new_ir.lvalue = new_var else: to_update = new_ir.lvalue while isinstance(to_update.points_to, ReferenceVariable): to_update = to_update.points_to to_update.points_to = new_var
def convert_expression_to_slithir(self): for func in self.functions + self.modifiers: func.generate_slithir_and_analyze() all_ssa_state_variables_instances = dict() for contract in self.inheritance: for v in contract.state_variables_declared: new_var = StateIRVariable(v) all_ssa_state_variables_instances[v.canonical_name] = new_var self._initial_state_variables.append(new_var) for v in self.variables: if v.contract == self: new_var = StateIRVariable(v) all_ssa_state_variables_instances[v.canonical_name] = new_var self._initial_state_variables.append(new_var) for func in self.functions + self.modifiers: func.generate_slithir_ssa(all_ssa_state_variables_instances)
def convert_expression_to_slithir_ssa(self): """ Assume generate_slithir_and_analyze was called on all functions :return: """ from slither.slithir.variables import StateIRVariable all_ssa_state_variables_instances = dict() for contract in self.inheritance: for v in contract.state_variables_declared: new_var = StateIRVariable(v) all_ssa_state_variables_instances[v.canonical_name] = new_var self._initial_state_variables.append(new_var) for v in self.variables: if v.contract == self: new_var = StateIRVariable(v) all_ssa_state_variables_instances[v.canonical_name] = new_var self._initial_state_variables.append(new_var) for func in self.functions + self.modifiers: func.generate_slithir_ssa(all_ssa_state_variables_instances)
def add_ssa_ir(function, all_state_variables_instances): ''' Add SSA version of the IR Args: function all_state_variables_instances ''' if not function.is_implemented: return init_definition = dict() for v in function.parameters + function.returns: if v.name: init_definition[v.name] = (v, function.entry_point) # We only add phi function for state variable at entry node if # The state variable is used # And if the state variables is written in another function (otherwise its stay at index 0) for (_, variable_instance) in all_state_variables_instances.items(): if is_used_later(function.entry_point, variable_instance): # rvalues are fixed in solc_parsing.declaration.function function.entry_point.add_ssa_ir( Phi(StateIRVariable(variable_instance), set())) add_phi_origins(function.entry_point, init_definition, dict()) for node in function.nodes: for (variable, nodes) in node.phi_origins_local_variables.values(): if len(nodes) < 2: continue if not is_used_later(node, variable): continue node.add_ssa_ir(Phi(LocalIRVariable(variable), nodes)) for (variable, nodes) in node.phi_origins_state_variables.values(): if len(nodes) < 2: continue #if not is_used_later(node, variable.name, []): # continue node.add_ssa_ir(Phi(StateIRVariable(variable), nodes)) init_local_variables_instances = dict() for v in function.parameters: if v.name: new_var = LocalIRVariable(v) function.add_parameter_ssa(new_var) if new_var.is_storage: fake_variable = LocalIRVariable(v) fake_variable.name = 'STORAGE_' + fake_variable.name fake_variable.set_location('reference_to_storage') new_var.refers_to = {fake_variable} init_local_variables_instances[ fake_variable.name] = fake_variable init_local_variables_instances[v.name] = new_var for v in function.returns: if v.name: new_var = LocalIRVariable(v) function.add_return_ssa(new_var) if new_var.is_storage: fake_variable = LocalIRVariable(v) fake_variable.name = 'STORAGE_' + fake_variable.name fake_variable.set_location('reference_to_storage') new_var.refers_to = {fake_variable} init_local_variables_instances[ fake_variable.name] = fake_variable init_local_variables_instances[v.name] = new_var all_init_local_variables_instances = dict(init_local_variables_instances) init_state_variables_instances = dict(all_state_variables_instances) initiate_all_local_variables_instances(function.nodes, init_local_variables_instances, all_init_local_variables_instances) generate_ssa_irs(function.entry_point, dict(init_local_variables_instances), all_init_local_variables_instances, dict(init_state_variables_instances), all_state_variables_instances, init_local_variables_instances, []) fix_phi_rvalues_and_storage_ref(function.entry_point, dict(init_local_variables_instances), all_init_local_variables_instances, dict(init_state_variables_instances), all_state_variables_instances, init_local_variables_instances)
def generate_ssa_irs(node, local_variables_instances, all_local_variables_instances, state_variables_instances, all_state_variables_instances, init_local_variables_instances, visited): if node in visited: return if node.type in [NodeType.ENDIF, NodeType.ENDLOOP] and any( not father in visited for father in node.fathers): return # visited is shared visited.append(node) for ir in node.irs_ssa: assert isinstance(ir, Phi) update_lvalue(ir, node, local_variables_instances, all_local_variables_instances, state_variables_instances, all_state_variables_instances) # these variables are lived only during the liveness of the block # They dont need phi function temporary_variables_instances = dict() reference_variables_instances = dict() for ir in node.irs: new_ir = copy_ir(ir, local_variables_instances, state_variables_instances, temporary_variables_instances, reference_variables_instances, all_local_variables_instances) update_lvalue(new_ir, node, local_variables_instances, all_local_variables_instances, state_variables_instances, all_state_variables_instances) if new_ir: node.add_ssa_ir(new_ir) if isinstance(ir, (InternalCall, HighLevelCall, InternalDynamicCall, LowLevelCall)): if isinstance(ir, LibraryCall): continue for variable in all_state_variables_instances.values(): if not is_used_later(node, variable): continue new_var = StateIRVariable(variable) new_var.index = all_state_variables_instances[ variable.canonical_name].index + 1 all_state_variables_instances[ variable.canonical_name] = new_var state_variables_instances[ variable.canonical_name] = new_var phi_ir = PhiCallback(new_var, {node}, new_ir, variable) # rvalues are fixed in solc_parsing.declaration.function node.add_ssa_ir(phi_ir) if isinstance(new_ir, (Assignment, Binary)): if isinstance(new_ir.lvalue, LocalIRVariable): if new_ir.lvalue.is_storage: if isinstance(new_ir.rvalue, ReferenceVariable): refers_to = new_ir.rvalue.points_to_origin new_ir.lvalue.add_refers_to(refers_to) else: new_ir.lvalue.add_refers_to(new_ir.rvalue) for succ in node.dominator_successors: generate_ssa_irs(succ, dict(local_variables_instances), all_local_variables_instances, dict(state_variables_instances), all_state_variables_instances, init_local_variables_instances, visited) for dominated in node.dominance_frontier: generate_ssa_irs(dominated, dict(local_variables_instances), all_local_variables_instances, dict(state_variables_instances), all_state_variables_instances, init_local_variables_instances, visited)
def generate_ssa_irs( node, local_variables_instances, all_local_variables_instances, state_variables_instances, all_state_variables_instances, init_local_variables_instances, visited, ): if node in visited: return if node.type in [NodeType.ENDIF, NodeType.ENDLOOP] and any( not father in visited for father in node.fathers): return # visited is shared visited.append(node) for ir in node.irs_ssa: assert isinstance(ir, Phi) update_lvalue( ir, node, local_variables_instances, all_local_variables_instances, state_variables_instances, all_state_variables_instances, ) # these variables are lived only during the liveness of the block # They dont need phi function temporary_variables_instances = {} reference_variables_instances = {} tuple_variables_instances = {} for ir in node.irs: new_ir = copy_ir( ir, local_variables_instances, state_variables_instances, temporary_variables_instances, reference_variables_instances, tuple_variables_instances, all_local_variables_instances, ) new_ir.set_expression(ir.expression) new_ir.set_node(ir.node) update_lvalue( new_ir, node, local_variables_instances, all_local_variables_instances, state_variables_instances, all_state_variables_instances, ) if new_ir: node.add_ssa_ir(new_ir) if isinstance(ir, (InternalCall, HighLevelCall, InternalDynamicCall, LowLevelCall)): if isinstance(ir, LibraryCall): continue for variable in all_state_variables_instances.values(): if not is_used_later(node, variable): continue new_var = StateIRVariable(variable) new_var.index = all_state_variables_instances[ variable.canonical_name].index + 1 all_state_variables_instances[ variable.canonical_name] = new_var state_variables_instances[ variable.canonical_name] = new_var phi_ir = PhiCallback(new_var, {node}, new_ir, variable) # rvalues are fixed in solc_parsing.declaration.function node.add_ssa_ir(phi_ir) if isinstance(new_ir, (Assignment, Binary)): if isinstance(new_ir.lvalue, LocalIRVariable): if new_ir.lvalue.is_storage: if isinstance(new_ir.rvalue, ReferenceVariable): refers_to = new_ir.rvalue.points_to_origin new_ir.lvalue.add_refers_to(refers_to) # Discard Constant # This can happen on yul, like # assembly { var.slot = some_value } # Here we do not keep track of the references as we do not track # such low level manipulation # However we could extend our storage model to do so in the future elif not isinstance(new_ir.rvalue, Constant): new_ir.lvalue.add_refers_to(new_ir.rvalue) for succ in node.dominator_successors: generate_ssa_irs( succ, dict(local_variables_instances), all_local_variables_instances, dict(state_variables_instances), all_state_variables_instances, init_local_variables_instances, visited, ) for dominated in node.dominance_frontier: generate_ssa_irs( dominated, dict(local_variables_instances), all_local_variables_instances, dict(state_variables_instances), all_state_variables_instances, init_local_variables_instances, visited, )