def _is_constant(f: Function) -> bool: # pylint: disable=too-many-branches """ Heuristic: - If view/pure with Solidity >= 0.4 -> Return true - If it contains assembly -> Return false (SlitherCore doesn't analyze asm) - Otherwise check for the rules from https://solidity.readthedocs.io/en/v0.5.0/contracts.html?highlight=pure#view-functions with an exception: internal dynamic call are not correctly handled, so we consider them as non-constant :param f: :return: """ if f.view or f.pure: if f.contract.slither.crytic_compile and f.contract.slither.crytic_compile.compiler_version: if not f.contract.slither.crytic_compile.compiler_version.version.startswith( "0.4"): return True if f.payable: return False if not f.is_implemented: return False if f.contains_assembly: return False if f.all_state_variables_written(): return False for ir in f.all_slithir_operations(): if isinstance(ir, InternalDynamicCall): return False if isinstance(ir, (EventCall, NewContract, LowLevelCall, Send, Transfer)): return False if isinstance(ir, SolidityCall) and ir.function in [ SolidityFunction("selfdestruct(address)"), SolidityFunction("suicide(address)"), ]: return False if isinstance(ir, HighLevelCall): if isinstance(ir.function, Variable) or ir.function.view or ir.function.pure: # External call to constant functions are ensured to be constant only for solidity >= 0.5 if (f.contract.slither.crytic_compile and f.contract.slither.crytic_compile.compiler_version): if f.contract.slither.crytic_compile.compiler_version.version.startswith( "0.4"): return False else: return False if isinstance(ir, InternalCall): # Storage write are not properly handled by all_state_variables_written if any(parameter.is_storage for parameter in ir.function.parameters): return False return True
def _analyze_function(self, function: Function, contract: Contract) -> List[Output]: results = [] for state_variable_written in function.state_variables_written: if state_variable_written.write_protection: for function_sig in state_variable_written.write_protection: function_protection = contract.get_function_from_signature( function_sig) if not function_protection: function_protection = contract.get_modifier_from_signature( function_sig) if not function_protection: self.logger.error(f"{function_sig} not found") continue if function_protection not in function.all_internal_calls( ): info = [ function, " should have ", function_protection, " to protect ", state_variable_written, "\n", ] res = self.generate_result(info) results.append(res) return results
def _use_modifier(function: Function, modifier_name: str = "whenNotPaused") -> bool: if function.is_constructor or function.view or function.pure: return False for internal_call in function.all_internal_calls(): if isinstance(internal_call, SolidityFunction): continue if any(modifier.name == modifier_name for modifier in function.modifiers): return True return False
def convert_yul_function_definition(root: YulScope, parent: YulNode, ast: Dict) -> YulNode: func = Function() yul_function = YulFunction(func, root, ast) root.contract.add_function(func) root.slither.add_function(func) root.add_yul_local_function(yul_function) yul_function.convert_body() yul_function.parse_body() return parent
def add_dependency(lvalue: Variable, function: Function, ir: Operation, is_protected: bool) -> None: if not lvalue in function.context[KEY_SSA]: function.context[KEY_SSA][lvalue] = set() if not is_protected: function.context[KEY_SSA_UNPROTECTED][lvalue] = set() if isinstance(ir, Index): read = [ir.variable_left] elif isinstance(ir, InternalCall): read = ir.function.return_values_ssa else: read = ir.read # pylint: disable=expression-not-assigned [ function.context[KEY_SSA][lvalue].add(v) for v in read if not isinstance(v, Constant) ] if not is_protected: [ function.context[KEY_SSA_UNPROTECTED][lvalue].add(v) for v in read if not isinstance(v, Constant) ]
def compute_dependency_function(function: Function) -> None: if KEY_SSA in function.context: return function.context[KEY_SSA] = {} function.context[KEY_SSA_UNPROTECTED] = {} is_protected = function.is_protected() for node in function.nodes: for ir in node.irs_ssa: if isinstance(ir, OperationWithLValue) and ir.lvalue: if isinstance(ir.lvalue, LocalIRVariable) and ir.lvalue.is_storage: continue if isinstance(ir.lvalue, ReferenceVariable): lvalue = ir.lvalue.points_to if lvalue: add_dependency(lvalue, function, ir, is_protected) add_dependency(ir.lvalue, function, ir, is_protected) function.context[KEY_NON_SSA] = convert_to_non_ssa( function.context[KEY_SSA]) function.context[KEY_NON_SSA_UNPROTECTED] = convert_to_non_ssa( function.context[KEY_SSA_UNPROTECTED])
def arbitrary_send(func: Function): if func.is_protected(): return [] ret: List[Node] = [] for node in func.nodes: for ir in node.irs: if isinstance(ir, SolidityCall): if ir.function == SolidityFunction("ecrecover(bytes32,uint8,bytes32,bytes32)"): return False if isinstance(ir, Index): if ir.variable_right == SolidityVariableComposed("msg.sender"): return False if is_dependent( ir.variable_right, SolidityVariableComposed("msg.sender"), func.contract, ): return False if isinstance(ir, (HighLevelCall, LowLevelCall, Transfer, Send)): if isinstance(ir, (HighLevelCall)): if isinstance(ir.function, Function): if ir.function.full_name == "transferFrom(address,address,uint256)": return False if ir.call_value is None: continue if ir.call_value == SolidityVariableComposed("msg.value"): continue if is_dependent( ir.call_value, SolidityVariableComposed("msg.value"), func.contract, ): continue if is_tainted(ir.destination, func.contract): ret.append(node) return ret
def __init__(self, func: Function, root: YulScope, ast: Dict): super().__init__(root.contract, root.id + [ast["name"]], parent_func=root.parent_func) assert ast["nodeType"] == "YulFunctionDefinition" self._function: Function = func self._root: YulScope = root self._ast: Dict = ast # start initializing the underlying function func.name = ast["name"] func.set_visibility("private") func.set_offset(ast["src"], root.slither) func.set_contract(root.contract) func.slither = root.slither func.set_contract_declarer(root.contract) func.scope = root.id func.is_implemented = True self._nodes: List[YulNode] = [] self._entrypoint = self.new_node(NodeType.ASSEMBLY, ast["src"]) func.entry_point = self._entrypoint.underlying_node self.add_yul_local_function(self)
def __init__( self, func: Function, root: YulScope, ast: Dict, node_scope: Union[Function, Scope] ): super().__init__(root.contract, root.id + [ast["name"]], parent_func=root.parent_func) assert ast["nodeType"] == "YulFunctionDefinition" self._function: Function = func self._root: YulScope = root self._ast: Dict = ast # start initializing the underlying function func.name = ast["name"] func.set_visibility("private") if isinstance(func, SourceMapping): func.set_offset(ast["src"], root.compilation_unit) if isinstance(func, FunctionContract): func.set_contract(root.contract) func.set_contract_declarer(root.contract) func.compilation_unit = root.compilation_unit func.internal_scope = root.id func.is_implemented = True self.node_scope = node_scope self._nodes: List[YulNode] = [] self._entrypoint = self.new_node(NodeType.ASSEMBLY, ast["src"]) func.entry_point = self._entrypoint.underlying_node self.add_yul_local_function(self)