예제 #1
0
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
예제 #3
0
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
예제 #4
0
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
예제 #5
0
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)
        ]
예제 #6
0
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
예제 #8
0
    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)
예제 #9
0
    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)