Esempio n. 1
0
    def _check(self):
        signatures_implem = get_signatures(self.contract)
        signatures_proxy = get_signatures(self.proxy)

        signatures_ids_implem = {get_function_id(s): s for s in signatures_implem}
        signatures_ids_proxy = {get_function_id(s): s for s in signatures_proxy}

        results = []

        for (k, _) in signatures_ids_implem.items():
            if k in signatures_ids_proxy:
                if signatures_ids_implem[k] != signatures_ids_proxy[k]:
                    implem_function = _get_function_or_variable(
                        self.contract, signatures_ids_implem[k]
                    )
                    proxy_function = _get_function_or_variable(self.proxy, signatures_ids_proxy[k])

                    info = [
                        "Function id collision found: ",
                        implem_function,
                        " ",
                        proxy_function,
                        "\n",
                    ]
                    json = self.generate_result(info)
                    results.append(json)

        return results
Esempio n. 2
0
def compare_function_ids(implem, implem_name, proxy, proxy_name):
    implem_contract = implem.get_contract_from_name(implem_name)
    if implem_contract is None:
        logger.info(red(f'{implem_name} not found in {implem.filename}'))
        return
    proxy_contract = proxy.get_contract_from_name(proxy_name)
    if proxy_contract is None:
        logger.info(red(f'{proxy_name} not found in {proxy.filename}'))
        return

    signatures_implem = get_signatures(implem_contract)
    signatures_proxy = get_signatures(proxy_contract)

    signatures_ids_implem = {get_function_id(s): s for s in signatures_implem}
    signatures_ids_proxy = {get_function_id(s): s for s in signatures_proxy}

    found = False
    for (k, _) in signatures_ids_implem.items():
        if k in signatures_ids_proxy:
            found = True
            logger.info(
                red('Function id collision found {} {}'.format(
                    signatures_ids_implem[k], signatures_ids_proxy[k])))

    if not found:
        logger.info(green('No function id collision found'))
def compare_function_ids(implem, implem_name, proxy, proxy_name):

    logger.info(green('Run function ids checks... (see https://github.com/crytic/slither/wiki/Upgradeability-Checks#functions-ids-checks)'))

    implem_contract = implem.get_contract_from_name(implem_name)
    if implem_contract is None:
        logger.info(red(f'{implem_name} not found in {implem.filename}'))
        return
    proxy_contract = proxy.get_contract_from_name(proxy_name)
    if proxy_contract is None:
        logger.info(red(f'{proxy_name} not found in {proxy.filename}'))
        return

    signatures_implem = get_signatures(implem_contract)
    signatures_proxy = get_signatures(proxy_contract)

    signatures_ids_implem = {get_function_id(s): s for s in signatures_implem}
    signatures_ids_proxy = {get_function_id(s): s for s in signatures_proxy}

    found = False
    for (k, _) in signatures_ids_implem.items():
        if k in signatures_ids_proxy:
            found = True
            if signatures_ids_implem[k] != signatures_ids_proxy[k]:
                logger.info(red('Function id collision found {} {}'.format(signatures_ids_implem[k],
                                                                           signatures_ids_proxy[k])))
            else:
                logger.info(red('Shadowing between proxy and implementation found {}'.format(signatures_ids_implem[k])))

    if not found:
        logger.info(green('No function ids collision found'))
Esempio n. 4
0
    def output(self, _filename):
        """
            _filename is not used
            Args:
                _filename(string)
        """

        txt = ''
        all_tables = []
        for contract in self.slither.contracts_derived:
            txt += '\n{}:\n'.format(contract.name)
            table = MyPrettyTable(['Name', 'ID'])
            for function in contract.functions:
                if function.visibility in ['public', 'external']:
                    table.add_row([
                        function.solidity_signature,
                        hex(get_function_id(function.solidity_signature))
                    ])
            for variable in contract.state_variables:
                if variable.visibility in ['public']:
                    sig = variable.function_name
                    table.add_row([sig, hex(get_function_id(sig))])
            txt += str(table) + '\n'
            all_tables.append((contract.name, table))

        self.info(txt)

        res = self.generate_output(txt)
        for name, table in all_tables:
            res.add_pretty_table(table, name)

        return res
    def output(self, _filename):
        """
        _filename is not used
        Args:
            _filename(string)
        """

        txt = ""
        all_tables = []
        for contract in self.slither.contracts_derived:
            txt += f"\n{contract.name}:\n"
            table = MyPrettyTable(["Name", "ID"])
            for function in contract.functions:
                if function.visibility in ["public", "external"]:
                    function_id = get_function_id(function.solidity_signature)
                    table.add_row([
                        function.solidity_signature, f"{function_id:#0{10}x}"
                    ])
            for variable in contract.state_variables:
                if variable.visibility in ["public"]:
                    sig = variable.function_name
                    function_id = get_function_id(sig)
                    table.add_row([sig, f"{function_id:#0{10}x}"])
            txt += str(table) + "\n"
            all_tables.append((contract.name, table))

        self.info(txt)

        res = self.generate_output(txt)
        for name, table in all_tables:
            res.add_pretty_table(table, name)

        return res
Esempio n. 6
0
    def output(self, _filename):
        """
            _filename is not used
            Args:
                _filename(string)
        """

        txt = ''
        for contract in self.slither.contracts_derived:
            txt += '\n{}:\n'.format(contract.name)
            table = PrettyTable(['Name', 'ID'])
            for function in contract.functions:
                if function.visibility in ['public', 'external']:
                    table.add_row([
                        function.full_name,
                        hex(get_function_id(function.full_name))
                    ])
            for variable in contract.state_variables:
                if variable.visibility in ['public']:
                    table.add_row([
                        variable.name + '()',
                        hex(get_function_id(variable.name + '()'))
                    ])
            txt += str(table) + '\n'

        self.info(txt)
Esempio n. 7
0
def compare_function_ids(implem, proxy):

    results = {
        'function-id-collision':[],
        'shadowing':[],
    }
    
    logger.info(green('\n## Run function ids checks... (see https://github.com/crytic/slither/wiki/Upgradeability-Checks#functions-ids-checks)'))

    signatures_implem = get_signatures(implem)
    signatures_proxy = get_signatures(proxy)

    signatures_ids_implem = {get_function_id(s): s for s in signatures_implem}
    signatures_ids_proxy = {get_function_id(s): s for s in signatures_proxy}

    error_found = False
    for (k, _) in signatures_ids_implem.items():
        if k in signatures_ids_proxy:
            error_found = True
            if signatures_ids_implem[k] != signatures_ids_proxy[k]:

                implem_function = _get_function_or_variable(implem, signatures_ids_implem[k])
                proxy_function = _get_function_or_variable(proxy, signatures_ids_proxy[k])

                info = f'Function id collision found: {implem_function.canonical_name} ({implem_function.source_mapping_str}) {proxy_function.canonical_name} ({proxy_function.source_mapping_str})'
                logger.info(red(info))
                res = Output(info)
                res.add(implem_function)
                res.add(proxy_function)
                results['function-id-collision'].append(res.data)
                
            else:

                implem_function = _get_function_or_variable(implem, signatures_ids_implem[k])
                proxy_function = _get_function_or_variable(proxy, signatures_ids_proxy[k])

                info = f'Shadowing between {implem_function.canonical_name} ({implem_function.source_mapping_str}) and {proxy_function.canonical_name} ({proxy_function.source_mapping_str})'
                logger.info(red(info))

                res = Output(info)
                res.add(implem_function)
                res.add(proxy_function)
                results['shadowing'].append(res.data)

    if not error_found:
        logger.info(green('No error found'))

    return results
Esempio n. 8
0
def _get_evm_instructions_function(function_info):
    function = function_info["function"]

    # CFG depends on function being constructor or not
    if function.is_constructor:
        cfg = function_info["contract_info"]["cfg_init"]
        # _dispatcher is the only function recognised by evm-cfg-builder in bytecode_init.
        # _dispatcher serves the role of the constructor in init code,
        #    given that there are no other functions.
        # Todo: Could rename it appropriately in evm-cfg-builder
        #    by detecting that init bytecode is being parsed.
        name = "_dispatcher"
        func_hash = ""
    else:
        cfg = function_info["contract_info"]["cfg"]
        name = function.name
        # Get first four bytes of function singature's keccak-256 hash used as function selector
        func_hash = str(hex(get_function_id(function.full_name)))

    function_evm = _get_function_evm(cfg, name, func_hash)
    if function_evm is None:
        to_log = "Function " + function.name + " not found in the EVM code"
        logger.error(to_log)
        raise SlitherError("Function " + function.name +
                           " not found in the EVM code")

    function_ins = []
    for basic_block in sorted(function_evm.basic_blocks,
                              key=lambda x: x.start.pc):
        for ins in basic_block.instructions:
            function_ins.append(ins)

    return function_ins
Esempio n. 9
0
def compare_function_ids(implem, proxy):

    signatures_implem = get_signatures(implem)
    signatures_proxy = get_signatures(proxy)

    signatures_ids_implem = {get_function_id(s): s for s in signatures_implem}
    signatures_ids_proxy = {get_function_id(s): s for s in signatures_proxy}

    found = False
    for (k, _) in signatures_ids_implem.items():
        if k in signatures_ids_proxy:
            found = True
            logger.info(
                red('Function id collision found {} {}'.format(
                    signatures_ids_implem[k], signatures_ids_proxy[k])))

    if not found:
        logger.info(green('No function id collision found'))
Esempio n. 10
0
def _convert_type_contract(ir, slither):
    assert isinstance(ir.variable_left.type, TypeInformation)
    contract = ir.variable_left.type.type

    if ir.variable_right == 'creationCode':
        if slither.crytic_compile:
            bytecode = slither.crytic_compile.bytecode_init(contract.name)
        else:
            logger.info(
                'The codebase uses type(x).creationCode, but crytic-compile was not used. As a result, the bytecode cannot be found'
            )
            bytecode = "MISSING_BYTECODE"
        assignment = Assignment(ir.lvalue, Constant(str(bytecode)),
                                ElementaryType('bytes'))
        assignment.set_expression(ir.expression)
        assignment.set_node(ir.node)
        assignment.lvalue.set_type(ElementaryType('bytes'))
        return assignment
    if ir.variable_right == 'runtimeCode':
        if slither.crytic_compile:
            bytecode = slither.crytic_compile.bytecode_runtime(contract.name)
        else:
            logger.info(
                'The codebase uses type(x).runtimeCode, but crytic-compile was not used. As a result, the bytecode cannot be found'
            )
            bytecode = "MISSING_BYTECODE"
        assignment = Assignment(ir.lvalue, Constant(str(bytecode)),
                                ElementaryType('bytes'))
        assignment.set_expression(ir.expression)
        assignment.set_node(ir.node)
        assignment.lvalue.set_type(ElementaryType('bytes'))
        return assignment
    if ir.variable_right == 'interfaceId':
        entry_points = contract.functions_entry_points
        interfaceId = 0
        for entry_point in entry_points:
            interfaceId = interfaceId ^ get_function_id(entry_point.full_name)
        assignment = Assignment(
            ir.lvalue, Constant(str(interfaceId),
                                type=ElementaryType('bytes4')),
            ElementaryType('bytes4'))
        assignment.set_expression(ir.expression)
        assignment.set_node(ir.node)
        assignment.lvalue.set_type(ElementaryType('bytes4'))
        return assignment

    if ir.variable_right == 'name':
        assignment = Assignment(ir.lvalue, Constant(contract.name),
                                ElementaryType('string'))
        assignment.set_expression(ir.expression)
        assignment.set_node(ir.node)
        assignment.lvalue.set_type(ElementaryType('string'))
        return assignment

    raise SlithIRError(f'type({contract.name}).{ir.variable_right} is unknown')
Esempio n. 11
0
    def _check(self):
        signatures_implem = get_signatures(self.contract)
        signatures_proxy = get_signatures(self.proxy)

        signatures_ids_implem = {get_function_id(s): s for s in signatures_implem}
        signatures_ids_proxy = {get_function_id(s): s for s in signatures_proxy}

        results = []

        for (k, _) in signatures_ids_implem.items():
            if k in signatures_ids_proxy:
                if signatures_ids_implem[k] == signatures_ids_proxy[k]:
                    implem_function = _get_function_or_variable(self.contract, signatures_ids_implem[k])
                    proxy_function = _get_function_or_variable(self.proxy, signatures_ids_proxy[k])

                    info = ['Function shadowing found: ', implem_function,
                            ' ', proxy_function, '\n']
                    json = self.generate_result(info)
                    results.append(json)

        return results
Esempio n. 12
0
    def output(self, _filename):
        """
            _filename is not used
            Args:
                _filename(string)
        """

        txt = ''
        for contract in self.slither.contracts_derived:
            txt += '\n{}:\n'.format(contract.name)
            table = PrettyTable(['Name', 'ID'])
            for function in contract.functions:
                if function.visibility in ['public', 'external']:
                    table.add_row([
                        function.full_name,
                        hex(get_function_id(function.full_name))
                    ])
            for variable in contract.state_variables:
                if variable.visibility in ['public']:
                    variable_getter_args = ""
                    if type(variable.type) is ArrayType:
                        length = 0
                        v = variable
                        while type(v.type) is ArrayType:
                            length += 1
                            v = v.type
                        variable_getter_args = ','.join(["uint256"] * length)
                    elif type(variable.type) is MappingType:
                        variable_getter_args = variable.type.type_from

                    table.add_row([
                        f"{variable.name}({variable_getter_args})",
                        hex(
                            get_function_id(
                                f"{variable.name}({variable_getter_args})"))
                    ])
            txt += str(table) + '\n'

        self.info(txt)
Esempio n. 13
0
    def _setter(self, function: Slither_Function, parent_contract):
        """
        Setting values when initializing

        Finished.
        """
        self._function_call_setter(function, parent_contract)
        self._add_constant_values_to_parameters()

        self._visibility = function.visibility
        self._view = True if function.view else False
        self._pure = True if function.pure else False
        self._payable = True if function.payable else False
        self._is_constructor = True if function.is_constructor else False
        self._sig_hash = get_function_id(function.solidity_signature)
        self._is_suicidal = self._check_is_suicidal()

        # load modifier objects.
        # requires within modifiers will be loaded into self.requires as well.
        self._load_modifiers(function)
        self._check_sat_by_default()
Esempio n. 14
0
def propagate_types(ir, node):
    # propagate the type
    using_for = node.function.contract.using_for
    if isinstance(ir, OperationWithLValue):
        # Force assignment in case of missing previous correct type
        if not ir.lvalue.type:
            if isinstance(ir, Assignment):
                ir.lvalue.set_type(ir.rvalue.type)
            elif isinstance(ir, Binary):
                if BinaryType.return_bool(ir.type):
                    ir.lvalue.set_type(ElementaryType('bool'))
                else:
                    ir.lvalue.set_type(ir.variable_left.type)
            elif isinstance(ir, Delete):
                # nothing to propagate
                pass
            elif isinstance(ir, LibraryCall):
                return convert_type_library_call(ir, ir.destination)
            elif isinstance(ir, HighLevelCall):
                t = ir.destination.type

                # Temporary operation (they are removed later)
                if t is None:
                    return

                if isinstance(t, ElementaryType) and t.name == 'address':
                    if can_be_solidity_func(ir):
                        return convert_to_solidity_func(ir)

                # convert library
                if t in using_for or '*' in using_for:
                    new_ir = convert_to_library(ir, node, using_for)
                    if new_ir:
                        return new_ir

                if isinstance(t, UserDefinedType):
                    # UserdefinedType
                    t_type = t.type
                    if isinstance(t_type, Contract):
                        contract = node.slither.get_contract_from_name(t_type.name)
                        return convert_type_of_high_and_internal_level_call(ir, contract)

                # Convert HighLevelCall to LowLevelCall
                if isinstance(t, ElementaryType) and t.name == 'address':
                    if ir.destination.name == 'this':
                        return convert_type_of_high_and_internal_level_call(ir, node.function.contract)
                    if can_be_low_level(ir):
                        return convert_to_low_level(ir)

                # Convert push operations
                # May need to insert a new operation
                # Which leads to return a list of operation
                if isinstance(t, ArrayType) or (isinstance(t, ElementaryType) and t.type == 'bytes'):
                    if ir.function_name == 'push' and len(ir.arguments) == 1:
                        return convert_to_push(ir, node)
                    if ir.function_name == 'pop' and len(ir.arguments) == 0:
                        return convert_to_pop(ir, node)

            elif isinstance(ir, Index):
                if isinstance(ir.variable_left.type, MappingType):
                    ir.lvalue.set_type(ir.variable_left.type.type_to)
                elif isinstance(ir.variable_left.type, ArrayType):
                    ir.lvalue.set_type(ir.variable_left.type.type)

            elif isinstance(ir, InitArray):
                length = len(ir.init_values)
                t = ir.init_values[0].type
                ir.lvalue.set_type(ArrayType(t, length))
            elif isinstance(ir, InternalCall):
                # if its not a tuple, return a singleton
                if ir.function is None:
                    convert_type_of_high_and_internal_level_call(ir, node.function.contract)
                return_type = ir.function.return_type
                if return_type:
                    if len(return_type) == 1:
                        ir.lvalue.set_type(return_type[0])
                    elif len(return_type) > 1:
                        ir.lvalue.set_type(return_type)
                else:
                    ir.lvalue = None
            elif isinstance(ir, InternalDynamicCall):
                # if its not a tuple, return a singleton
                return_type = ir.function_type.return_type
                if return_type:
                    if len(return_type) == 1:
                        ir.lvalue.set_type(return_type[0])
                    else:
                        ir.lvalue.set_type(return_type)
                else:
                    ir.lvalue = None
            elif isinstance(ir, LowLevelCall):
                # Call are not yet converted
                # This should not happen
                assert False
            elif isinstance(ir, Member):
                # TODO we should convert the reference to a temporary if the member is a length or a balance
                if ir.variable_right == 'length' and not isinstance(ir.variable_left, Contract) and isinstance(
                        ir.variable_left.type, (ElementaryType, ArrayType)):
                    length = Length(ir.variable_left, ir.lvalue)
                    length.set_expression(ir.expression)
                    length.lvalue.points_to = ir.variable_left
                    length.set_node(ir.node)
                    return length
                if ir.variable_right == 'balance' and not isinstance(ir.variable_left, Contract) and isinstance(
                        ir.variable_left.type, ElementaryType):
                    b = Balance(ir.variable_left, ir.lvalue)
                    b.set_expression(ir.expression)
                    b.set_node(ir.node)
                    return b
                if ir.variable_right == 'selector' and isinstance(ir.variable_left.type, Function):
                    assignment = Assignment(ir.lvalue,
                                            Constant(str(get_function_id(ir.variable_left.type.full_name))),
                                            ElementaryType('bytes4'))
                    assignment.set_expression(ir.expression)
                    assignment.set_node(ir.node)
                    assignment.lvalue.set_type(ElementaryType('bytes4'))
                    return assignment
                if isinstance(ir.variable_left, TemporaryVariable) and isinstance(ir.variable_left.type,
                                                                                  TypeInformation):
                    return _convert_type_contract(ir, node.function.slither)
                left = ir.variable_left
                t = None
                if isinstance(left, (Variable, SolidityVariable)):
                    t = ir.variable_left.type
                elif isinstance(left, (Contract, Enum, Structure)):
                    t = UserDefinedType(left)
                # can be None due to temporary operation
                if t:
                    if isinstance(t, UserDefinedType):
                        # UserdefinedType
                        type_t = t.type
                        if isinstance(type_t, Enum):
                            ir.lvalue.set_type(t)
                        elif isinstance(type_t, Structure):
                            elems = type_t.elems
                            for elem in elems:
                                if elem == ir.variable_right:
                                    ir.lvalue.set_type(elems[elem].type)
                        else:
                            assert isinstance(type_t, Contract)
                            # Allow type propagtion as a Function
                            # Only for reference variables
                            # This allows to track the selector keyword
                            # We dont need to check for function collision, as solc prevents the use of selector
                            # if there are multiple functions with the same name
                            f = next((f for f in type_t.functions if f.name == ir.variable_right), None)
                            if f:
                                ir.lvalue.set_type(f)
                            else:
                                # Allow propgation for variable access through contract's nale
                                # like Base_contract.my_variable
                                v = next((v for v in type_t.state_variables if v.name == ir.variable_right), None)
                                if v:
                                    ir.lvalue.set_type(v.type)
            elif isinstance(ir, NewArray):
                ir.lvalue.set_type(ir.array_type)
            elif isinstance(ir, NewContract):
                contract = node.slither.get_contract_from_name(ir.contract_name)
                ir.lvalue.set_type(UserDefinedType(contract))
            elif isinstance(ir, NewElementaryType):
                ir.lvalue.set_type(ir.type)
            elif isinstance(ir, NewStructure):
                ir.lvalue.set_type(UserDefinedType(ir.structure))
            elif isinstance(ir, Push):
                # No change required
                pass
            elif isinstance(ir, Send):
                ir.lvalue.set_type(ElementaryType('bool'))
            elif isinstance(ir, SolidityCall):
                if ir.function.name == 'type(address)':
                    ir.function.return_type = [TypeInformation(ir.arguments[0])]
                return_type = ir.function.return_type
                if len(return_type) == 1:
                    ir.lvalue.set_type(return_type[0])
                elif len(return_type) > 1:
                    ir.lvalue.set_type(return_type)
            elif isinstance(ir, TypeConversion):
                ir.lvalue.set_type(ir.type)
            elif isinstance(ir, Unary):
                ir.lvalue.set_type(ir.rvalue.type)
            elif isinstance(ir, Unpack):
                types = ir.tuple.type.type
                idx = ir.index
                t = types[idx]
                ir.lvalue.set_type(t)
            elif isinstance(ir,
                            (Argument, TmpCall, TmpNewArray, TmpNewContract, TmpNewStructure, TmpNewElementaryType)):
                # temporary operation; they will be removed
                pass
            else:
                raise SlithIRError('Not handling {} during type propgation'.format(type(ir)))