Ejemplo n.º 1
0
    def taint_balance_equalities(self, functions):
        taints = []
        for func in functions:
            for node in func.nodes:
                for ir in node.irs_ssa:
                    if isinstance(ir, Balance):
                        taints.append(ir.lvalue)
                    if isinstance(ir, HighLevelCall):
                        # print(ir.function.full_name)
                        if (isinstance(ir.function, Function) and
                                ir.function.full_name == "balanceOf(address)"):
                            taints.append(ir.lvalue)
                        if (isinstance(ir.function, StateVariable)
                                and isinstance(ir.function.type, MappingType)
                                and ir.function.name == "balanceOf"
                                and ir.function.type.type_from
                                == ElementaryType("address")
                                and ir.function.type.type_to
                                == ElementaryType("uint256")):
                            taints.append(ir.lvalue)
                    if isinstance(ir, Assignment):
                        if ir.rvalue in self.sources_taint:
                            taints.append(ir.lvalue)

        return taints
Ejemplo n.º 2
0
def convert_to_low_level(ir):
    """
        Convert to a transfer/send/or low level call
        The funciton assume to receive a correct IR
        The checks must be done by the caller

        Must be called after can_be_low_level
    """
    if ir.function_name == 'transfer':
        assert len(ir.arguments) == 1
        prev_ir = ir
        ir = Transfer(ir.destination, ir.arguments[0])
        ir.set_expression(prev_ir.expression)
        return ir
    elif ir.function_name == 'send':
        assert len(ir.arguments) == 1
        prev_ir = ir
        ir = Send(ir.destination, ir.arguments[0], ir.lvalue)
        ir.set_expression(prev_ir.expression)
        ir.lvalue.set_type(ElementaryType('bool'))
        return ir
    elif ir.function_name in [
            'call', 'delegatecall', 'callcode', 'staticcall'
    ]:
        new_ir = LowLevelCall(ir.destination, ir.function_name,
                              ir.nbr_arguments, ir.lvalue, ir.type_call)
        new_ir.call_gas = ir.call_gas
        new_ir.call_value = ir.call_value
        new_ir.arguments = ir.arguments
        new_ir.lvalue.set_type(ElementaryType('bool'))
        new_ir.set_expression(ir.expression)
        return new_ir
    raise SlithIRError('Incorrect conversion to low level {}'.format(ir))
Ejemplo n.º 3
0
    def _post_binary_operation(self, expression):
        left = get(expression.expression_left)
        right = get(expression.expression_right)
        val = TemporaryVariable(self._node)

        if expression.type in _signed_to_unsigned:
            new_left = TemporaryVariable(self._node)
            conv_left = TypeConversion(new_left, left, ElementaryType('int256'))
            conv_left.set_expression(expression)
            self._result.append(conv_left)

            if expression.type != BinaryOperationType.RIGHT_SHIFT_ARITHMETIC:
                new_right = TemporaryVariable(self._node)
                conv_right = TypeConversion(new_right, right, ElementaryType('int256'))
                conv_right.set_expression(expression)
                self._result.append(conv_right)
            else:
                new_right = right

            new_final = TemporaryVariable(self._node)
            operation = Binary(new_final, new_left, new_right, _signed_to_unsigned[expression.type])
            operation.set_expression(expression)
            self._result.append(operation)

            conv_final = TypeConversion(val, new_final, ElementaryType('uint256'))
            conv_final.set_expression(expression)
            self._result.append(conv_final)
        else:
            operation = Binary(val, left, right, _binary_to_binary[expression.type])
            operation.set_expression(expression)
            self._result.append(operation)

        set_val(expression, val)
Ejemplo n.º 4
0
def convert_arguments(arguments):
    argss = [[]]
    for arg in arguments:
        if isinstance(arg, (list,)):
            type_arg = '{}[{}]'.format(get_type(arg[0].type), len(arg))
        elif isinstance(arg, Function):
            type_arg = arg.signature_str
        else:
            type_arg = get_type(arg.type)
        if isinstance(arg, Constant) and arg.type == ElementaryType('uint256'):
            # If it is a constant
            # We dupplicate the existing list
            # And we add uint256 and int256 cases
            # There is no potential collision, as the compiler
            # Prevent it with a 
            # "not unique after argument-dependent loopkup" issue
            argss_new = [list(args) for args in argss]
            for args in argss:
                args.append(str(ElementaryType('uint256')))
            for args in argss_new:
                args.append(str(ElementaryType('int256')))
            argss = argss + argss_new
        else:
            for args in argss:
                args.append(type_arg)
    return argss
Ejemplo n.º 5
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')
Ejemplo n.º 6
0
def is_upgradable_gap_variable(contract: Contract,
                               variable: StateVariable) -> bool:
    """Helper function that returns true if 'variable' is a gap variable used
    for upgradable contracts. More specifically, the function returns true if:
     - variable is named "__gap"
     - it is a uint256 array declared at the end of the contract
     - it has private visibility"""

    # Return early on if the variable name is != gap to avoid iterating over all the state variables
    if variable.name != "__gap":
        return False

    declared_variable_ordered = [
        v for v in contract.state_variables_ordered
        if v in contract.state_variables_declared
    ]

    if not declared_variable_ordered:
        return False

    variable_type = variable.type
    return (declared_variable_ordered[-1] is variable
            and isinstance(variable_type, ArrayType)
            and variable_type.type == ElementaryType("uint256")
            and variable.visibility == "private")
Ejemplo n.º 7
0
 def __init__(self, value, lvalue):
     super().__init__()
     assert is_valid_rvalue(value)
     assert is_valid_lvalue(lvalue)
     self._value = value
     self._lvalue = lvalue
     lvalue.set_type(ElementaryType("uint256"))
Ejemplo n.º 8
0
 def __init__(self, name: str):
     assert name in SOLIDITY_FUNCTIONS
     self._name = name
     # Can be TypeInformation if type(address) is used
     self._return_type: List[Union[TypeInformation, ElementaryType]] = [
         ElementaryType(x) for x in SOLIDITY_FUNCTIONS[self.name]
     ]
Ejemplo n.º 9
0
def convert_to_low_level(ir):
    """
        Convert to a transfer/send/or low level call
        The funciton assume to receive a correct IR
        The checks must be done by the caller

        Additionally convert abi... to solidityfunction
    """
    if ir.function_name == 'transfer':
        assert len(ir.arguments) == 1
        ir = Transfer(ir.destination, ir.arguments[0])
        return ir
    elif ir.function_name == 'send':
        assert len(ir.arguments) == 1
        ir = Send(ir.destination, ir.arguments[0], ir.lvalue)
        ir.lvalue.set_type(ElementaryType('bool'))
        return ir
    elif ir.destination.name ==  'abi' and ir.function_name in ['encode',
                                                                'encodePacked',
                                                                'encodeWithSelector',
                                                                'encodeWithSignature',
                                                                'decode']:

        call = SolidityFunction('abi.{}()'.format(ir.function_name))
        new_ir = SolidityCall(call, ir.nbr_arguments, ir.lvalue, ir.type_call)
        new_ir.arguments = ir.arguments
        if isinstance(call.return_type, list) and len(call.return_type) == 1:
            new_ir.lvalue.set_type(call.return_type[0])
        else:
            new_ir.lvalue.set_type(call.return_type)
        return new_ir
    elif ir.function_name in ['call',
                              'delegatecall',
                              'callcode',
                              'staticcall']:
        new_ir = LowLevelCall(ir.destination,
                          ir.function_name,
                          ir.nbr_arguments,
                          ir.lvalue,
                          ir.type_call)
        new_ir.call_gas = ir.call_gas
        new_ir.call_value = ir.call_value
        new_ir.arguments = ir.arguments
        new_ir.lvalue.set_type(ElementaryType('bool'))
        return new_ir
    logger.error('Incorrect conversion to low level {}'.format(ir))
    exit(-1)
 def __init__(self, import_directive: Import):
     super().__init__()
     assert import_directive.alias is not None
     self._import_directive = import_directive
     self._name = import_directive.alias
     self._type = ElementaryType("string")
     self._initialized = True
     self._visibility = "private"
     self._is_constant = True
Ejemplo n.º 11
0
def parse_yul_literal(root: YulScope, node: YulNode,
                      ast: Dict) -> Optional[Expression]:
    type_ = ast['type']
    value = ast['value']

    if not type_:
        type_ = 'bool' if value in ['true', 'false'] else 'uint256'

    return Literal(value, ElementaryType(type_))
Ejemplo n.º 12
0
def parse_yul_function_call(root: YulScope, node: YulNode,
                            ast: Dict) -> Optional[Expression]:
    args = [parse_yul(root, node, arg) for arg in ast["arguments"]]
    ident = parse_yul(root, node, ast["functionName"])

    if not isinstance(ident, Identifier):
        raise SlitherException(
            "expected identifier from parsing function name")

    if isinstance(ident.value, YulBuiltin):
        name = ident.value.name
        if name in binary_ops:
            if name in ["shl", "shr", "sar"]:
                # lmao ok
                return BinaryOperation(args[1], args[0], binary_ops[name])

            return BinaryOperation(args[0], args[1], binary_ops[name])

        if name in unary_ops:
            return UnaryOperation(args[0], unary_ops[name])

        if name == "stop":
            name = "return"
            ident = Identifier(
                SolidityFunction(format_function_descriptor(name)))
            args = [
                Literal("0", ElementaryType("uint256")),
                Literal("0", ElementaryType("uint256")),
            ]

        else:
            ident = Identifier(
                SolidityFunction(format_function_descriptor(ident.value.name)))

    if isinstance(ident.value, Function):
        return CallExpression(ident, args,
                              vars_to_typestr(ident.value.returns))
    if isinstance(ident.value, SolidityFunction):
        return CallExpression(ident, args,
                              vars_to_typestr(ident.value.return_type))

    raise SlitherException(
        f"unexpected function call target type {str(type(ident.value))}")
Ejemplo n.º 13
0
def parse_yul_literal(_root: YulScope, _node: YulNode, ast: Dict) -> Optional[Expression]:
    kind = ast["kind"]
    value = ast["value"]

    if not kind:
        kind = "bool" if value in ["true", "false"] else "uint256"

    if kind == "number":
        kind = "uint256"

    return Literal(value, ElementaryType(kind))
Ejemplo n.º 14
0
 def __init__(self, result, left_variable, right_variable, operation_type):
     assert is_valid_rvalue(left_variable)
     assert is_valid_rvalue(right_variable)
     assert is_valid_lvalue(result)
     super(Binary, self).__init__()
     self._variables = [left_variable, right_variable]
     self._type = operation_type
     self._lvalue = result
     if BinaryType.return_bool(operation_type):
         result.set_type(ElementaryType('bool'))
     else:
         result.set_type(left_variable.type)
Ejemplo n.º 15
0
 def __init__(self, result, left_variable, right_variable, operation_type: BinaryType):
     assert is_valid_rvalue(left_variable) or isinstance(left_variable, Function)
     assert is_valid_rvalue(right_variable) or isinstance(right_variable, Function)
     assert is_valid_lvalue(result)
     assert isinstance(operation_type, BinaryType)
     super().__init__()
     self._variables = [left_variable, right_variable]
     self._type = operation_type
     self._lvalue = result
     if BinaryType.return_bool(operation_type):
         result.set_type(ElementaryType("bool"))
     else:
         result.set_type(left_variable.type)
Ejemplo n.º 16
0
    def __init__(self, var: LocalVariable, root: YulScope, ast: Dict):
        assert ast["nodeType"] == "YulTypedName"

        self._variable = var
        self._root = root

        # start initializing the underlying variable
        var.set_function(root.function)
        var.set_offset(ast["src"], root.slither)

        var.name = _name_to_yul_name(ast["name"], root.id)
        var.set_type(ElementaryType("uint256"))
        var.set_location("memory")
Ejemplo n.º 17
0
    def __init__(self, var: LocalVariable, root: YulScope, ast: Dict):
        assert (ast['nodeType'] == 'YulTypedName')

        self._variable = var
        self._root = root

        # start initializing the underlying variable
        var.set_function(root.function)
        var.set_offset(ast["src"], root.slither)

        var.name = ast['name']
        var.set_type(ElementaryType('uint256'))
        var.set_location('memory')
Ejemplo n.º 18
0
def get_sig(ir):
    '''
        Return a list of potential signature
        It is a list, as Constant variables can be converted to int256
    Args:
        ir (slithIR.operation)
    Returns:
        list(str)
    '''
    sig = '{}({})'
    name = ir.function_name

    # list of list of arguments
    argss = [[]]
    for arg in ir.arguments:
        if isinstance(arg, (list, )):
            type_arg = '{}[{}]'.format(get_type(arg[0].type), len(arg))
        elif isinstance(arg, Function):
            type_arg = arg.signature_str
        else:
            type_arg = get_type(arg.type)
        if isinstance(arg, Constant) and arg.type == ElementaryType('uint256'):
            # If it is a constant
            # We dupplicate the existing list
            # And we add uint256 and int256 cases
            # There is no potential collision, as the compiler
            # Prevent it with a
            # "not unique after argument-dependent loopkup" issue
            argss_new = [list(args) for args in argss]
            for args in argss:
                args.append(str(ElementaryType('uint256')))
            for args in argss_new:
                args.append(str(ElementaryType('int256')))
            argss = argss + argss_new
        else:
            for args in argss:
                args.append(type_arg)
    return [sig.format(name, ','.join(args)) for args in argss]
    def _detect_boolean_constant_misuses(contract):  # pylint: disable=too-many-branches
        """
        Detects and returns all nodes which misuse a Boolean constant.
        :param contract: Contract to detect assignment within.
        :return: A list of misusing nodes.
        """

        # Create our result set.
        results = []

        # Loop for each function and modifier.
        for function in contract.functions_declared:  # pylint: disable=too-many-nested-blocks
            f_results = set()

            # Loop for every node in this function, looking for boolean constants
            for node in function.nodes:

                # Do not report "while(true)"
                if node.type == NodeType.IFLOOP:
                    if node.irs:
                        if len(node.irs) == 1:
                            ir = node.irs[0]
                            if isinstance(ir, Condition) and ir.value == Constant(
                                "True", ElementaryType("bool")
                            ):
                                continue

                for ir in node.irs:
                    if isinstance(ir, (Assignment, Call, Return, InitArray)):
                        # It's ok to use a bare boolean constant in these contexts
                        continue
                    if isinstance(ir, Binary):
                        if ir.type in [
                            BinaryType.ADDITION,
                            BinaryType.EQUAL,
                            BinaryType.NOT_EQUAL,
                        ]:
                            # Comparing to a Boolean constant is dubious style, but harmless
                            # Equal is catch by another detector (informational severity)
                            continue
                    for r in ir.read:
                        if isinstance(r, Constant):
                            if isinstance(r.value, bool):
                                f_results.add(node)
                results.append((function, f_results))

        # Return the resulting set of nodes with improper uses of Boolean constants
        return results
Ejemplo n.º 20
0
def convert_to_pop(ir, node):
    """
    Convert pop operators
    Return a list of 6 operations
    """

    ret = []

    arr = ir.destination
    length = ReferenceVariable(node)
    length.set_type(ElementaryType('uint256'))

    ir_length = Length(arr, length)
    ir_length.set_expression(ir.expression)
    ir_length.set_node(ir.node)
    ir_length.lvalue.points_to = arr
    ret.append(ir_length)

    val = TemporaryVariable(node)

    ir_sub_1 = Binary(val, length, Constant("1", ElementaryType('uint256')),
                      BinaryType.SUBTRACTION)
    ir_sub_1.set_expression(ir.expression)
    ir_sub_1.set_node(ir.node)
    ret.append(ir_sub_1)

    element_to_delete = ReferenceVariable(node)
    ir_assign_element_to_delete = Index(element_to_delete, arr, val,
                                        ElementaryType('uint256'))
    ir_length.lvalue.points_to = arr
    element_to_delete.set_type(ElementaryType('uint256'))
    ir_assign_element_to_delete.set_expression(ir.expression)
    ir_assign_element_to_delete.set_node(ir.node)
    ret.append(ir_assign_element_to_delete)

    ir_delete = Delete(element_to_delete, element_to_delete)
    ir_delete.set_expression(ir.expression)
    ir_delete.set_node(ir.node)
    ret.append(ir_delete)

    length_to_assign = ReferenceVariable(node)
    length_to_assign.set_type(ElementaryType('uint256'))
    ir_length = Length(arr, length_to_assign)
    ir_length.set_expression(ir.expression)
    ir_length.lvalue.points_to = arr
    ir_length.set_node(ir.node)
    ret.append(ir_length)

    ir_assign_length = Assignment(length_to_assign, val,
                                  ElementaryType('uint256'))
    ir_assign_length.set_expression(ir.expression)
    ir_assign_length.set_node(ir.node)
    ret.append(ir_assign_length)

    return ret
Ejemplo n.º 21
0
 def is_conditional(self, include_loop=True) -> bool:
     """
         Check if the node is a conditional node
         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
     """
     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
Ejemplo n.º 22
0
    def _parse_type_alias(self, item: Dict) -> None:
        assert "name" in item
        assert "underlyingType" in item
        underlying_type = item["underlyingType"]
        assert "nodeType" in underlying_type and underlying_type["nodeType"] == "ElementaryTypeName"
        assert "name" in underlying_type

        original_type = ElementaryType(underlying_type["name"])

        # For user defined types defined at the contract level the lookup can be done
        # Using the name or the canonical name
        # For example during the type parsing the canonical name
        # Note that Solidity allows shadowing of user defined types
        # Between top level and contract definitions
        alias = item["name"]
        alias_canonical = self._contract.name + "." + item["name"]

        user_defined_type = TypeAliasContract(original_type, alias, self.underlying_contract)
        user_defined_type.set_offset(item["src"], self.compilation_unit)
        self._contract.file_scope.user_defined_types[alias] = user_defined_type
        self._contract.file_scope.user_defined_types[alias_canonical] = user_defined_type
Ejemplo n.º 23
0
 def __init__(self, value, lvalue):
     assert is_valid_rvalue(value)
     assert is_valid_lvalue(lvalue)
     self._value = value
     self._lvalue = lvalue
     lvalue.set_type(ElementaryType('uint256'))
Ejemplo n.º 24
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)))
Ejemplo n.º 25
0
 def type(self) -> ElementaryType:
     return ElementaryType(SOLIDITY_VARIABLES_COMPOSED[self.name])
Ejemplo n.º 26
0
    def _post_call_expression(self, expression):
        called = get(expression.called)
        args = [get(a) for a in expression.arguments if a]
        for arg in args:
            arg_ = Argument(arg)
            arg_.set_expression(expression)
            self._result.append(arg_)
        if isinstance(called, Function):
            # internal call

            # If tuple
            if expression.type_call.startswith('tuple(') and expression.type_call != 'tuple()':
                val = TupleVariable(self._node)
            else:
                val = TemporaryVariable(self._node)
            internal_call = InternalCall(called, len(args), val, expression.type_call)
            internal_call.set_expression(expression)
            self._result.append(internal_call)
            set_val(expression, val)
        else:
            # yul things
            if called.name == 'caller()':
                val = TemporaryVariable(self._node)
                var = Assignment(val, SolidityVariableComposed('msg.sender'), 'uint256')
                self._result.append(var)
                set_val(expression, val)
            elif called.name == 'origin()':
                val = TemporaryVariable(self._node)
                var = Assignment(val, SolidityVariableComposed('tx.origin'), 'uint256')
                self._result.append(var)
                set_val(expression, val)
            elif called.name == 'extcodesize(uint256)':
                val = ReferenceVariable(self._node)
                var = Member(args[0], Constant('codesize'), val)
                self._result.append(var)
                set_val(expression, val)
            elif called.name == 'selfbalance()':
                val = TemporaryVariable(self._node)
                var = TypeConversion(val, SolidityVariable('this'), ElementaryType('address'))
                self._result.append(var)

                val1 = ReferenceVariable(self._node)
                var1 = Member(val, Constant('balance'), val1)
                self._result.append(var1)
                set_val(expression, val1)
            elif called.name == 'address()':
                val = TemporaryVariable(self._node)
                var = TypeConversion(val, SolidityVariable('this'), ElementaryType('address'))
                self._result.append(var)
                set_val(expression, val)
            elif called.name == 'callvalue()':
                val = TemporaryVariable(self._node)
                var = Assignment(val, SolidityVariableComposed('msg.value'), 'uint256')
                self._result.append(var)
                set_val(expression, val)
            else:
                # If tuple
                if expression.type_call.startswith('tuple(') and expression.type_call != 'tuple()':
                    val = TupleVariable(self._node)
                else:
                    val = TemporaryVariable(self._node)

                message_call = TmpCall(called, len(args), val, expression.type_call)
                message_call.set_expression(expression)
                # Gas/value are only accessible here if the syntax {gas: , value: }
                # Is used over .gas().value()
                if expression.call_gas:
                    call_gas = get(expression.call_gas)
                    message_call.call_gas = call_gas
                if expression.call_value:
                    call_value = get(expression.call_value)
                    message_call.call_value = call_value
                if expression.call_salt:
                    call_salt = get(expression.call_salt)
                    message_call.call_salt = call_salt
                self._result.append(message_call)
                set_val(expression, val)
Ejemplo n.º 27
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

                # 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_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_level_call(
                            ir, node.function.contract)
                    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):
                    if ir.function_name == 'push' and len(ir.arguments) == 1:
                        return convert_to_push(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
                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.lvalue.points_to = ir.variable_left
                    return length
                if ir.variable_right == 'balance' and not isinstance(
                        ir.variable_left, Contract) and isinstance(
                            ir.variable_left.type, ElementaryType):
                    return Balance(ir.variable_left, ir.lvalue)
                left = ir.variable_left
                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)
            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):
                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:
                logger.error('Not handling {} during type propgation'.format(
                    type(ir)))
                exit(-1)
Ejemplo n.º 28
0
 def type(self):
     return ElementaryType(SOLIDITY_VARIABLES[self.name])
Ejemplo n.º 29
0
 def return_type(self):
     return [ElementaryType(x) for x in SOLIDITY_FUNCTIONS[self.name]]
Ejemplo n.º 30
0
def parse_expression(expression, caller_context):
    """

    Returns:
        str: expression
    """
    #  Expression
    #    = Expression ('++' | '--')
    #    | NewExpression
    #    | IndexAccess
    #    | MemberAccess
    #    | FunctionCall
    #    | '(' Expression ')'
    #    | ('!' | '~' | 'delete' | '++' | '--' | '+' | '-') Expression
    #    | Expression '**' Expression
    #    | Expression ('*' | '/' | '%') Expression
    #    | Expression ('+' | '-') Expression
    #    | Expression ('<<' | '>>') Expression
    #    | Expression '&' Expression
    #    | Expression '^' Expression
    #    | Expression '|' Expression
    #    | Expression ('<' | '>' | '<=' | '>=') Expression
    #    | Expression ('==' | '!=') Expression
    #    | Expression '&&' Expression
    #    | Expression '||' Expression
    #    | Expression '?' Expression ':' Expression
    #    | Expression ('=' | '|=' | '^=' | '&=' | '<<=' | '>>=' | '+=' | '-=' | '*=' | '/=' | '%=') Expression
    #    | PrimaryExpression

    # The AST naming does not follow the spec
    name = expression[caller_context.get_key()]
    is_compact_ast = caller_context.is_compact_ast

    if name == 'UnaryOperation':
        if is_compact_ast:
            attributes = expression
        else:
            attributes = expression['attributes']
        assert 'prefix' in attributes
        operation_type = UnaryOperationType.get_type(attributes['operator'],
                                                     attributes['prefix'])

        if is_compact_ast:
            expression = parse_expression(expression['subExpression'],
                                          caller_context)
        else:
            assert len(expression['children']) == 1
            expression = parse_expression(expression['children'][0],
                                          caller_context)
        unary_op = UnaryOperation(expression, operation_type)
        return unary_op

    elif name == 'BinaryOperation':
        if is_compact_ast:
            attributes = expression
        else:
            attributes = expression['attributes']
        operation_type = BinaryOperationType.get_type(attributes['operator'])

        if is_compact_ast:
            left_expression = parse_expression(expression['leftExpression'],
                                               caller_context)
            right_expression = parse_expression(expression['rightExpression'],
                                                caller_context)
        else:
            assert len(expression['children']) == 2
            left_expression = parse_expression(expression['children'][0],
                                               caller_context)
            right_expression = parse_expression(expression['children'][1],
                                                caller_context)
        binary_op = BinaryOperation(left_expression, right_expression,
                                    operation_type)
        return binary_op

    elif name == 'FunctionCall':
        return parse_call(expression, caller_context)

    elif name == 'TupleExpression':
        """
            For expression like
            (a,,c) = (1,2,3)
            the AST provides only two children in the left side
            We check the type provided (tuple(uint256,,uint256))
            To determine that there is an empty variable
            Otherwhise we would not be able to determine that
            a = 1, c = 3, and 2 is lost

            Note: this is only possible with Solidity >= 0.4.12
        """
        if is_compact_ast:
            expressions = [
                parse_expression(e, caller_context) if e else None
                for e in expression['components']
            ]
        else:
            if 'children' not in expression:
                attributes = expression['attributes']
                components = attributes['components']
                expressions = [
                    parse_expression(c, caller_context) if c else None
                    for c in components
                ]
            else:
                expressions = [
                    parse_expression(e, caller_context)
                    for e in expression['children']
                ]
        # Add none for empty tuple items
        if "attributes" in expression:
            if "type" in expression['attributes']:
                t = expression['attributes']['type']
                if ',,' in t or '(,' in t or ',)' in t:
                    t = t[len('tuple('):-1]
                    elems = t.split(',')
                    for idx in range(len(elems)):
                        if elems[idx] == '':
                            expressions.insert(idx, None)
        t = TupleExpression(expressions)
        return t

    elif name == 'Conditional':
        if is_compact_ast:
            if_expression = parse_expression(expression['condition'],
                                             caller_context)
            then_expression = parse_expression(expression['trueExpression'],
                                               caller_context)
            else_expression = parse_expression(expression['falseExpression'],
                                               caller_context)
        else:
            children = expression['children']
            assert len(children) == 3
            if_expression = parse_expression(children[0], caller_context)
            then_expression = parse_expression(children[1], caller_context)
            else_expression = parse_expression(children[2], caller_context)
        conditional = ConditionalExpression(if_expression, then_expression,
                                            else_expression)
        return conditional

    elif name == 'Assignment':
        if is_compact_ast:
            left_expression = parse_expression(expression['leftHandSide'],
                                               caller_context)
            right_expression = parse_expression(expression['rightHandSide'],
                                                caller_context)

            operation_type = AssignmentOperationType.get_type(
                expression['operator'])

            operation_return_type = expression['typeDescriptions'][
                'typeString']
        else:
            attributes = expression['attributes']
            children = expression['children']
            assert len(expression['children']) == 2
            left_expression = parse_expression(children[0], caller_context)
            right_expression = parse_expression(children[1], caller_context)

            operation_type = AssignmentOperationType.get_type(
                attributes['operator'])
            operation_return_type = attributes['type']

        assignement = AssignmentOperation(left_expression, right_expression,
                                          operation_type,
                                          operation_return_type)
        return assignement

    elif name == 'Literal':
        assert 'children' not in expression

        if is_compact_ast:
            value = expression['value']
            if not value:
                value = '0x' + expression['hexValue']
        else:
            value = expression['attributes']['value']
            if value is None:
                # for literal declared as hex
                # see https://solidity.readthedocs.io/en/v0.4.25/types.html?highlight=hex#hexadecimal-literals
                assert 'hexvalue' in expression['attributes']
                value = '0x' + expression['attributes']['hexvalue']
        literal = Literal(value)
        return literal

    elif name == 'Identifier':
        assert 'children' not in expression

        t = None

        if caller_context.is_compact_ast:
            value = expression['name']
            t = expression['typeDescriptions']['typeString']
        else:
            value = expression['attributes']['value']
            if 'type' in expression['attributes']:
                t = expression['attributes']['type']

        if t:
            found = re.findall(
                '[struct|enum|function|modifier] \(([\[\] ()a-zA-Z0-9\.,_]*)\)',
                t)
            assert len(found) <= 1
            if found:
                value = value + '(' + found[0] + ')'
                value = filter_name(value)

        var = find_variable(value, caller_context)

        identifier = Identifier(var)
        return identifier

    elif name == 'IndexAccess':
        if is_compact_ast:
            index_type = expression['typeDescriptions']['typeString']
            left = expression['baseExpression']
            right = expression['indexExpression']
        else:
            index_type = expression['attributes']['type']
            children = expression['children']
            assert len(children) == 2
            left = children[0]
            right = children[1]
        left_expression = parse_expression(left, caller_context)
        right_expression = parse_expression(right, caller_context)
        index = IndexAccess(left_expression, right_expression, index_type)
        return index

    elif name == 'MemberAccess':
        if caller_context.is_compact_ast:
            member_name = expression['memberName']
            member_type = expression['typeDescriptions']['typeString']
            member_expression = parse_expression(expression['expression'],
                                                 caller_context)
        else:
            member_name = expression['attributes']['member_name']
            member_type = expression['attributes']['type']
            children = expression['children']
            assert len(children) == 1
            member_expression = parse_expression(children[0], caller_context)
        if str(member_expression) == 'super':
            super_name = parse_super_name(expression, is_compact_ast)
            if isinstance(caller_context, Contract):
                inheritance = caller_context.inheritance
            else:
                assert isinstance(caller_context, Function)
                inheritance = caller_context.contract.inheritance
            var = None
            for father in inheritance:
                try:
                    var = find_variable(super_name, father)
                    break
                except VariableNotFound:
                    continue
            if var is None:
                raise VariableNotFound(
                    'Variable not found: {}'.format(super_name))
            return SuperIdentifier(var)
        member_access = MemberAccess(member_name, member_type,
                                     member_expression)
        if str(member_access) in SOLIDITY_VARIABLES_COMPOSED:
            return Identifier(SolidityVariableComposed(str(member_access)))
        return member_access

    elif name == 'ElementaryTypeNameExpression':
        # nop exression
        # uint;
        if is_compact_ast:
            value = expression['typeName']
        else:
            assert 'children' not in expression
            value = expression['attributes']['value']
        t = parse_type(UnknownType(value), caller_context)

        return ElementaryTypeNameExpression(t)

    # NewExpression is not a root expression, it's always the child of another expression
    elif name == 'NewExpression':

        if is_compact_ast:
            type_name = expression['typeName']
        else:
            children = expression['children']
            assert len(children) == 1
            type_name = children[0]

        if type_name[caller_context.get_key()] == 'ArrayTypeName':
            depth = 0
            while type_name[caller_context.get_key()] == 'ArrayTypeName':
                # Note: dont conserve the size of the array if provided
                # We compute it directly
                if is_compact_ast:
                    type_name = type_name['baseType']
                else:
                    type_name = type_name['children'][0]
                depth += 1
            if type_name[caller_context.get_key()] == 'ElementaryTypeName':
                if is_compact_ast:
                    array_type = ElementaryType(type_name['name'])
                else:
                    array_type = ElementaryType(
                        type_name['attributes']['name'])
            elif type_name[caller_context.get_key()] == 'UserDefinedTypeName':
                if is_compact_ast:
                    array_type = parse_type(UnknownType(type_name['name']),
                                            caller_context)
                else:
                    array_type = parse_type(
                        UnknownType(type_name['attributes']['name']),
                        caller_context)
            else:
                logger.error('Incorrect type array {}'.format(type_name))
                exit(-1)
            array = NewArray(depth, array_type)
            return array

        if type_name[caller_context.get_key()] == 'ElementaryTypeName':
            if is_compact_ast:
                elem_type = ElementaryType(type_name['name'])
            else:
                elem_type = ElementaryType(type_name['attributes']['name'])
            new_elem = NewElementaryType(elem_type)
            return new_elem

        assert type_name[caller_context.get_key()] == 'UserDefinedTypeName'

        if is_compact_ast:
            contract_name = type_name['name']
        else:
            contract_name = type_name['attributes']['name']
        new = NewContract(contract_name)
        return new

    elif name == 'ModifierInvocation':

        if is_compact_ast:
            called = parse_expression(expression['modifierName'],
                                      caller_context)
            arguments = []
            if expression['arguments']:
                arguments = [
                    parse_expression(a, caller_context)
                    for a in expression['arguments']
                ]
        else:
            children = expression['children']
            called = parse_expression(children[0], caller_context)
            arguments = [
                parse_expression(a, caller_context) for a in children[1::]
            ]

        call = CallExpression(called, arguments, 'Modifier')
        return call

    logger.error('Expression not parsed %s' % name)
    exit(-1)