Exemplo n.º 1
0
    def __init__(self, val, constant_type=None, subdenomination=None):  # pylint: disable=too-many-branches
        super().__init__()
        assert isinstance(val, str)

        self._original_value = val
        self._subdenomination = subdenomination

        if subdenomination:
            val = str(convert_subdenomination(val, subdenomination))

        if constant_type:  # pylint: disable=too-many-nested-blocks
            assert isinstance(constant_type, ElementaryType)
            self._type = constant_type
            if constant_type.type in Int + Uint + ["address"]:
                self._val = convert_string_to_int(val)
            elif constant_type.type == "bool":
                self._val = (val == "true") | (val == "True")
            else:
                self._val = val
        else:
            if val.isdigit():
                self._type = ElementaryType("uint256")
                self._val = int(Decimal(val))
            else:
                self._type = ElementaryType("string")
                self._val = val
Exemplo n.º 2
0
    def __init__(self, val, type=None):
        super(Constant, self).__init__()
        assert isinstance(val, str)

        self._original_value = val

        if type:
            assert isinstance(type, ElementaryType)
            self._type = type
            if type.type in Int + Uint + ['address']:
                if val.startswith('0x') or val.startswith('0X'):
                    self._val = int(val, 16)
                else:
                    if 'e' in val:
                        base, expo = val.split('e')
                        self._val = int(float(base) * (10**int(expo)))
                    elif 'E' in val:
                        base, expo = val.split('E')
                        self._val = int(float(base) * (10**int(expo)))
                    else:
                        self._val = int(float(val))
            elif type.type == 'bool':
                self._val = val == 'true'
            else:
                self._val = val
        else:
            if val.isdigit():
                self._type = ElementaryType('uint256')
                self._val = int(val)
            else:
                self._type = ElementaryType('string')
                self._val = val
Exemplo n.º 3
0
 def __init__(self, val):
     super(Constant, self).__init__()
     assert isinstance(val, str)
     if val.isdigit():
         self._type = ElementaryType('uint256')
         self._val = int(val)
     else:
         self._type = ElementaryType('string')
         self._val = val
Exemplo n.º 4
0
    def _detect_missing_zero_address_validation(self, contract):
        """
        Detects if addresses are zero address validated before use.
        :param contract: The contract to check
        :return: Functions with nodes where addresses used are not zero address validated earlier
        """
        results = []

        for function in contract.functions_entry_points:
            var_nodes = defaultdict(list)

            for node in function.nodes:
                sv_addrs_written = [
                    sv
                    for sv in node.state_variables_written
                    if sv.type == ElementaryType("address")
                ]

                addr_calls = False
                for ir in node.irs:
                    if isinstance(ir, (Send, Transfer, LowLevelCall)):
                        addr_calls = True

                # Continue if no address-typed state variables are written and if no send/transfer/call
                if not sv_addrs_written and not addr_calls:
                    continue

                # Check local variables used in such nodes
                for var in node.local_variables_read:
                    # Check for address types that are tainted but not by msg.sender
                    if var.type == ElementaryType("address") and is_tainted(
                        var, function, ignore_generic_taint=True
                    ):
                        # Check for zero address validation of variable
                        # in the context of modifiers used or prior function context
                        if not (
                            self._zero_address_validation_in_modifier(
                                var, function.modifiers_statements
                            )
                            or self._zero_address_validation(var, node, [])
                        ):
                            # Report a variable only once per function
                            var_nodes[var].append(node)
            if var_nodes:
                results.append((function, var_nodes))
        return results
Exemplo n.º 5
0
    def _init_from_declaration(self, var, init):
        if self._is_compact_ast:
            attributes = var
            self._typeName = attributes['typeDescriptions']['typeString']
        else:
            assert len(var['children']) <= 2
            assert var['name'] == 'VariableDeclaration'

            attributes = var['attributes']
            self._typeName = attributes['type']

        self._name = attributes['name']
        self._arrayDepth = 0
        self._isMapping = False
        self._mappingFrom = None
        self._mappingTo = False
        self._initial_expression = None
        self._type = None

        if 'constant' in attributes:
            self._is_constant = attributes['constant']

        self._analyze_variable_attributes(attributes)

        if self._is_compact_ast:
            if var['typeName']:
                self._elem_to_parse = var['typeName']
            else:
                self._elem_to_parse = UnknownType(
                    var['typeDescriptions']['typeString'])
        else:
            if not var['children']:
                # It happens on variable declared inside loop declaration
                try:
                    self._type = ElementaryType(self._typeName)
                    self._elem_to_parse = None
                except NonElementaryType:
                    self._elem_to_parse = UnknownType(self._typeName)
            else:
                self._elem_to_parse = var['children'][0]

        if self._is_compact_ast:
            self._initializedNotParsed = init
            if init:
                self._initialized = True
        else:
            if init:  # there are two way to init a var local in the AST
                assert len(var['children']) <= 1
                self._initialized = True
                self._initializedNotParsed = init
            elif len(var['children']) in [0, 1]:
                self._initialized = False
                self._initializedNotParsed = []
            else:
                assert len(var['children']) == 2
                self._initialized = True
                self._initializedNotParsed = var['children'][1]
 def __init__(self, variable: Slither_Local_Variable):
     super().__init__(variable)
     self._name = variable.name
     if isinstance(variable.type, UserDefinedType):
         if isinstance(variable.type.type, ContractSolc04):
             self._type = ElementaryType('address')
         else:
             self._type = variable.type
     else:
         self._type = variable.type
Exemplo n.º 7
0
    def __init__(self, val, type=None, subdenomination=None):
        super(Constant, self).__init__()
        assert isinstance(val, str)

        self._original_value = val
        self._subdenomination = subdenomination

        if subdenomination:
            val = str(convert_subdenomination(val, subdenomination))

        if type:
            assert isinstance(type, ElementaryType)
            self._type = type
            if type.type in Int + Uint + ['address']:
                if val.startswith('0x') or val.startswith('0X'):
                    self._val = int(val, 16)
                else:
                    if 'e' in val:
                        base, expo = val.split('e')
                        self._val = int(Decimal(base) * (10**int(expo)))
                    elif 'E' in val:
                        base, expo = val.split('E')
                        self._val = int(Decimal(base) * (10**int(expo)))
                    else:
                        self._val = int(Decimal(val))
            elif type.type == 'bool':
                self._val = (val == 'true') | (val == 'True')
            else:
                self._val = val
        else:
            if val.isdigit():
                self._type = ElementaryType('uint256')
                self._val = int(Decimal(val))
            else:
                self._type = ElementaryType('string')
                self._val = val
    def _detect_missing_events(contract):
        """
        Detects if critical contract parameters set by owners and used in access control are missing events
        :param contract: The contract to check
        :return: Functions with nodes of critical operations but no events
        """
        results = []

        # pylint: disable=too-many-nested-blocks
        for function in contract.functions_entry_points:
            nodes = []

            # Check for any events in the function and skip if found
            # Note: not checking if event corresponds to critical parameter
            if any(ir for node in function.nodes for ir in node.irs
                   if isinstance(ir, EventCall)):
                continue

            # Ignore constructors and private/internal functions
            # Heuristic-1: functions with critical operations are typically "protected". Skip unprotected functions.
            if function.is_constructor or not function.is_protected():
                continue

            # Heuristic-2: Critical operations are where state variables are written and tainted
            # Heuristic-3: Variables of interest are address type that are used in modifiers i.e. access control
            # Heuristic-4: Critical operations present but no events in the function is not a good practice
            for node in function.nodes:
                for sv in node.state_variables_written:
                    if is_tainted(
                            sv,
                            function) and sv.type == ElementaryType("address"):
                        for mod in function.contract.modifiers:
                            if sv in mod.state_variables_read:
                                nodes.append((node, sv, mod))
            if nodes:
                results.append((function, nodes))
        return results
Exemplo n.º 9
0
def _find_from_type_name(name, contract, contracts, structures, enums):
    name_elementary = name.split(' ')[0]
    if '[' in name_elementary:
        name_elementary = name_elementary[0:name_elementary.find('[')]
    if name_elementary in ElementaryTypeName:
        depth = name.count('[')
        if depth:
            return ArrayType(ElementaryType(name_elementary),
                             Literal(depth, 'uint256'))
        else:
            return ElementaryType(name_elementary)
    # We first look for contract
    # To avoid collision
    # Ex: a structure with the name of a contract
    name_contract = name
    if name_contract.startswith('contract '):
        name_contract = name_contract[len('contract '):]
    if name_contract.startswith('library '):
        name_contract = name_contract[len('library '):]
    var_type = next((c for c in contracts if c.name == name_contract), None)

    if not var_type:
        var_type = next((st for st in structures if st.name == name), None)
    if not var_type:
        var_type = next((e for e in enums if e.name == name), None)
    if not var_type:
        # any contract can refer to another contract's enum
        enum_name = name
        if enum_name.startswith('enum '):
            enum_name = enum_name[len('enum '):]
        all_enums = [c.enums for c in contracts]
        all_enums = [item for sublist in all_enums for item in sublist]
        var_type = next((e for e in all_enums if e.name == enum_name), None)
        if not var_type:
            var_type = next(
                (e for e in all_enums if e.canonical_name == enum_name), None)
    if not var_type:
        # any contract can refer to another contract's structure
        name_struct = name
        if name_struct.startswith('struct '):
            name_struct = name_struct[len('struct '):]
            name_struct = name_struct.split(' ')[
                0]  # remove stuff like storage pointer at the end
        all_structures = [c.structures for c in contracts]
        all_structures = [
            item for sublist in all_structures for item in sublist
        ]
        var_type = next(
            (st for st in all_structures if st.name == name_struct), None)
        if not var_type:
            var_type = next(
                (st
                 for st in all_structures if st.canonical_name == name_struct),
                None)
        # case where struct xxx.xx[] where not well formed in the AST
        if not var_type:
            depth = 0
            while name_struct.endswith('[]'):
                name_struct = name_struct[0:-2]
                depth += 1
            var_type = next(
                (st
                 for st in all_structures if st.canonical_name == name_struct),
                None)
            if var_type:
                return ArrayType(UserDefinedType(var_type),
                                 Literal(depth, 'uint256'))

    if not var_type:
        var_type = next((f for f in contract.functions if f.name == name),
                        None)
    if not var_type:
        if name.startswith('function '):
            found = re.findall(
                'function \(([ ()a-zA-Z0-9\.,]*)\) returns \(([a-zA-Z0-9\.,]*)\)',
                name)
            assert len(found) == 1
            params = found[0][0].split(',')
            return_values = found[0][1].split(',')
            params = [
                _find_from_type_name(p, contract, contracts, structures, enums)
                for p in params
            ]
            return_values = [
                _find_from_type_name(r, contract, contracts, structures, enums)
                for r in return_values
            ]
            params_vars = []
            return_vars = []
            for p in params:
                var = FunctionTypeVariable()
                var.set_type(p)
                params_vars.append(var)
            for r in return_values:
                var = FunctionTypeVariable()
                var.set_type(r)
                return_vars.append(var)
            return FunctionType(params_vars, return_vars)
    if not var_type:
        if name.startswith('mapping('):
            # nested mapping declared with var
            if name.count('mapping(') == 1:
                found = re.findall(
                    'mapping\(([a-zA-Z0-9\.]*) => ([a-zA-Z0-9\.\[\]]*)\)',
                    name)
            else:
                found = re.findall(
                    'mapping\(([a-zA-Z0-9\.]*) => (mapping\([=> a-zA-Z0-9\.\[\]]*\))\)',
                    name)
            assert len(found) == 1
            from_ = found[0][0]
            to_ = found[0][1]

            from_type = _find_from_type_name(from_, contract, contracts,
                                             structures, enums)
            to_type = _find_from_type_name(to_, contract, contracts,
                                           structures, enums)

            return MappingType(from_type, to_type)

    if not var_type:
        raise ParsingError('Type not found ' + str(name))
    return UserDefinedType(var_type)
Exemplo n.º 10
0
def parse_type(t, caller_context):
    # local import to avoid circular dependency
    from slither.solc_parsing.expressions.expression_parsing import parse_expression
    from slither.solc_parsing.variables.function_type_variable import FunctionTypeVariableSolc

    if isinstance(caller_context, Contract):
        contract = caller_context
    elif isinstance(caller_context, Function):
        contract = caller_context.contract
    else:
        raise ParsingError('Incorrect caller context')

    is_compact_ast = caller_context.is_compact_ast

    if is_compact_ast:
        key = 'nodeType'
    else:
        key = 'name'

    structures = contract.structures
    enums = contract.enums
    contracts = contract.slither.contracts

    if isinstance(t, UnknownType):
        return _find_from_type_name(t.name, contract, contracts, structures,
                                    enums)

    elif t[key] == 'ElementaryTypeName':
        if is_compact_ast:
            return ElementaryType(t['name'])
        return ElementaryType(t['attributes'][key])

    elif t[key] == 'UserDefinedTypeName':
        if is_compact_ast:
            return _find_from_type_name(t['typeDescriptions']['typeString'],
                                        contract, contracts, structures, enums)

        # Determine if we have a type node (otherwise we use the name node, as some older solc did not have 'type').
        type_name_key = 'type' if 'type' in t['attributes'] else key
        return _find_from_type_name(t['attributes'][type_name_key], contract,
                                    contracts, structures, enums)

    elif t[key] == 'ArrayTypeName':
        length = None
        if is_compact_ast:
            if t['length']:
                length = parse_expression(t['length'], caller_context)
            array_type = parse_type(t['baseType'], contract)
        else:
            if len(t['children']) == 2:
                length = parse_expression(t['children'][1], caller_context)
            else:
                assert len(t['children']) == 1
            array_type = parse_type(t['children'][0], contract)
        return ArrayType(array_type, length)

    elif t[key] == 'Mapping':

        if is_compact_ast:
            mappingFrom = parse_type(t['keyType'], contract)
            mappingTo = parse_type(t['valueType'], contract)
        else:
            assert len(t['children']) == 2

            mappingFrom = parse_type(t['children'][0], contract)
            mappingTo = parse_type(t['children'][1], contract)

        return MappingType(mappingFrom, mappingTo)

    elif t[key] == 'FunctionTypeName':

        if is_compact_ast:
            params = t['parameterTypes']
            return_values = t['returnParameterTypes']
            index = 'parameters'
        else:
            assert len(t['children']) == 2
            params = t['children'][0]
            return_values = t['children'][1]
            index = 'children'

        assert params[key] == 'ParameterList'
        assert return_values[key] == 'ParameterList'

        params_vars = []
        return_values_vars = []
        for p in params[index]:
            var = FunctionTypeVariableSolc(p)
            var.set_offset(p['src'], caller_context.slither)
            var.analyze(caller_context)
            params_vars.append(var)
        for p in return_values[index]:
            var = FunctionTypeVariableSolc(p)

            var.set_offset(p['src'], caller_context.slither)
            var.analyze(caller_context)
            return_values_vars.append(var)

        return FunctionType(params_vars, return_values_vars)

    raise ParsingError('Type name not found ' + str(t))
    def _init_from_declaration(self, var: Dict, init: bool):  # pylint: disable=too-many-branches
        if self._is_compact_ast:
            attributes = var
            self._typeName = attributes["typeDescriptions"]["typeString"]
        else:
            assert len(var["children"]) <= 2
            assert var["name"] == "VariableDeclaration"

            attributes = var["attributes"]
            self._typeName = attributes["type"]

        self._variable.name = attributes["name"]
        # self._arrayDepth = 0
        # self._isMapping = False
        # self._mappingFrom = None
        # self._mappingTo = False
        # self._initial_expression = None
        # self._type = None

        # Only for comapct ast format
        # the id can be used later if referencedDeclaration
        # is provided
        if "id" in var:
            self._reference_id = var["id"]

        if "constant" in attributes:
            self._variable.is_constant = attributes["constant"]

        self._analyze_variable_attributes(attributes)

        if self._is_compact_ast:
            if var["typeName"]:
                self._elem_to_parse = var["typeName"]
            else:
                self._elem_to_parse = UnknownType(
                    var["typeDescriptions"]["typeString"])
        else:
            if not var["children"]:
                # It happens on variable declared inside loop declaration
                try:
                    self._variable.type = ElementaryType(self._typeName)
                    self._elem_to_parse = None
                except NonElementaryType:
                    self._elem_to_parse = UnknownType(self._typeName)
            else:
                self._elem_to_parse = var["children"][0]

        if self._is_compact_ast:
            self._initializedNotParsed = init
            if init:
                self._variable.initialized = True
        else:
            if init:  # there are two way to init a var local in the AST
                assert len(var["children"]) <= 1
                self._variable.initialized = True
                self._initializedNotParsed = init
            elif len(var["children"]) in [0, 1]:
                self._variable.initialized = False
                self._initializedNotParsed = []
            else:
                assert len(var["children"]) == 2
                self._variable.initialized = True
                self._initializedNotParsed = var["children"][1]
Exemplo n.º 12
0
def _find_from_type_name(  # pylint: disable=too-many-locals,too-many-branches,too-many-statements,too-many-arguments
    name: str,
    functions_direct_access: List["Function"],
    contracts_direct_access: List["Contract"],
    structures_direct_access: List["Structure"],
    all_structures: List["Structure"],
    enums_direct_access: List["Enum"],
    all_enums: List["Enum"],
) -> Type:
    name_elementary = name.split(" ")[0]
    if "[" in name_elementary:
        name_elementary = name_elementary[0 : name_elementary.find("[")]
    if name_elementary in ElementaryTypeName:
        depth = name.count("[")
        if depth:
            return ArrayType(ElementaryType(name_elementary), Literal(depth, "uint256"))
        return ElementaryType(name_elementary)
    # We first look for contract
    # To avoid collision
    # Ex: a structure with the name of a contract
    name_contract = name
    if name_contract.startswith("contract "):
        name_contract = name_contract[len("contract ") :]
    if name_contract.startswith("library "):
        name_contract = name_contract[len("library ") :]
    var_type = next((c for c in contracts_direct_access if c.name == name_contract), None)

    if not var_type:
        var_type = next((st for st in structures_direct_access if st.name == name), None)
    if not var_type:
        var_type = next((e for e in enums_direct_access if e.name == name), None)
    if not var_type:
        # any contract can refer to another contract's enum
        enum_name = name
        if enum_name.startswith("enum "):
            enum_name = enum_name[len("enum ") :]
        elif enum_name.startswith("type(enum"):
            enum_name = enum_name[len("type(enum ") : -1]
        # all_enums = [c.enums for c in contracts]
        # all_enums = [item for sublist in all_enums for item in sublist]
        # all_enums += contract.slither.enums_top_level
        var_type = next((e for e in all_enums if e.name == enum_name), None)
        if not var_type:
            var_type = next((e for e in all_enums if e.canonical_name == enum_name), None)
    if not var_type:
        # any contract can refer to another contract's structure
        name_struct = name
        if name_struct.startswith("struct "):
            name_struct = name_struct[len("struct ") :]
            name_struct = name_struct.split(" ")[0]  # remove stuff like storage pointer at the end
        # all_structures = [c.structures for c in contracts]
        # all_structures = [item for sublist in all_structures for item in sublist]
        # all_structures += contract.slither.structures_top_level
        var_type = next((st for st in all_structures if st.name == name_struct), None)
        if not var_type:
            var_type = next((st for st in all_structures if st.canonical_name == name_struct), None)
        # case where struct xxx.xx[] where not well formed in the AST
        if not var_type:
            depth = 0
            while name_struct.endswith("[]"):
                name_struct = name_struct[0:-2]
                depth += 1
            var_type = next((st for st in all_structures if st.canonical_name == name_struct), None)
            if var_type:
                return ArrayType(UserDefinedType(var_type), Literal(depth, "uint256"))

    if not var_type:
        var_type = next((f for f in functions_direct_access if f.name == name), None)
    if not var_type:
        if name.startswith("function "):
            found = re.findall(
                "function \(([ ()\[\]a-zA-Z0-9\.,]*?)\)(?: payable)?(?: (?:external|internal|pure|view))?(?: returns \(([a-zA-Z0-9() \.,]*)\))?",
                name,
            )
            assert len(found) == 1
            params = [v for v in found[0][0].split(",") if v != ""]
            return_values = (
                [v for v in found[0][1].split(",") if v != ""] if len(found[0]) > 1 else []
            )
            params = [
                _find_from_type_name(
                    p,
                    functions_direct_access,
                    contracts_direct_access,
                    structures_direct_access,
                    all_structures,
                    enums_direct_access,
                    all_enums,
                )
                for p in params
            ]
            return_values = [
                _find_from_type_name(
                    r,
                    functions_direct_access,
                    contracts_direct_access,
                    structures_direct_access,
                    all_structures,
                    enums_direct_access,
                    all_enums,
                )
                for r in return_values
            ]
            params_vars = []
            return_vars = []
            for p in params:
                var = FunctionTypeVariable()
                var.set_type(p)
                params_vars.append(var)
            for r in return_values:
                var = FunctionTypeVariable()
                var.set_type(r)
                return_vars.append(var)
            return FunctionType(params_vars, return_vars)
    if not var_type:
        if name.startswith("mapping("):
            # nested mapping declared with var
            if name.count("mapping(") == 1:
                found = re.findall("mapping\(([a-zA-Z0-9\.]*) => ([ a-zA-Z0-9\.\[\]]*)\)", name)
            else:
                found = re.findall(
                    "mapping\(([a-zA-Z0-9\.]*) => (mapping\([=> a-zA-Z0-9\.\[\]]*\))\)",
                    name,
                )
            assert len(found) == 1
            from_ = found[0][0]
            to_ = found[0][1]

            from_type = _find_from_type_name(
                from_,
                functions_direct_access,
                contracts_direct_access,
                structures_direct_access,
                all_structures,
                enums_direct_access,
                all_enums,
            )
            to_type = _find_from_type_name(
                to_,
                functions_direct_access,
                contracts_direct_access,
                structures_direct_access,
                all_structures,
                enums_direct_access,
                all_enums,
            )

            return MappingType(from_type, to_type)

    if not var_type:
        raise ParsingError("Type not found " + str(name))
    return UserDefinedType(var_type)
Exemplo n.º 13
0
def parse_type(t: Union[Dict, UnknownType], caller_context):
    # local import to avoid circular dependency
    # pylint: disable=too-many-locals,too-many-branches,too-many-statements
    # pylint: disable=import-outside-toplevel
    from slither.solc_parsing.expressions.expression_parsing import parse_expression
    from slither.solc_parsing.variables.function_type_variable import FunctionTypeVariableSolc
    from slither.solc_parsing.declarations.contract import ContractSolc
    from slither.solc_parsing.declarations.function import FunctionSolc
    from slither.solc_parsing.slitherSolc import SlitherSolc

    # Note: for convenicence top level functions use the same parser than function in contract
    # but contract_parser is set to None
    if isinstance(caller_context, SlitherSolc) or (
        isinstance(caller_context, FunctionSolc) and caller_context.contract_parser is None
    ):
        if isinstance(caller_context, SlitherSolc):
            sl = caller_context.core
            next_context = caller_context
        else:
            assert isinstance(caller_context, FunctionSolc)
            sl = caller_context.underlying_function.slither
            next_context = caller_context.slither_parser
        structures_direct_access = sl.structures_top_level
        all_structuress = [c.structures for c in sl.contracts]
        all_structures = [item for sublist in all_structuress for item in sublist]
        all_structures += structures_direct_access
        enums_direct_access = sl.enums_top_level
        all_enumss = [c.enums for c in sl.contracts]
        all_enums = [item for sublist in all_enumss for item in sublist]
        all_enums += enums_direct_access
        contracts = sl.contracts
        functions = []
    elif isinstance(caller_context, (ContractSolc, FunctionSolc)):
        if isinstance(caller_context, FunctionSolc):
            underlying_func = caller_context.underlying_function
            # If contract_parser is set to None, then underlying_function is a functionContract
            # See note above
            assert isinstance(underlying_func, FunctionContract)
            contract = underlying_func.contract
            next_context = caller_context.contract_parser
        else:
            contract = caller_context.underlying_contract
            next_context = caller_context

        structures_direct_access = contract.structures + contract.slither.structures_top_level
        all_structuress = [c.structures for c in contract.slither.contracts]
        all_structures = [item for sublist in all_structuress for item in sublist]
        all_structures += contract.slither.structures_top_level
        enums_direct_access = contract.enums + contract.slither.enums_top_level
        all_enumss = [c.enums for c in contract.slither.contracts]
        all_enums = [item for sublist in all_enumss for item in sublist]
        all_enums += contract.slither.enums_top_level
        contracts = contract.slither.contracts
        functions = contract.functions + contract.modifiers
    else:
        raise ParsingError(f"Incorrect caller context: {type(caller_context)}")

    is_compact_ast = caller_context.is_compact_ast
    if is_compact_ast:
        key = "nodeType"
    else:
        key = "name"

    if isinstance(t, UnknownType):
        return _find_from_type_name(
            t.name,
            functions,
            contracts,
            structures_direct_access,
            all_structures,
            enums_direct_access,
            all_enums,
        )

    if t[key] == "ElementaryTypeName":
        if is_compact_ast:
            return ElementaryType(t["name"])
        return ElementaryType(t["attributes"][key])

    if t[key] == "UserDefinedTypeName":
        if is_compact_ast:
            return _find_from_type_name(
                t["typeDescriptions"]["typeString"],
                functions,
                contracts,
                structures_direct_access,
                all_structures,
                enums_direct_access,
                all_enums,
            )

        # Determine if we have a type node (otherwise we use the name node, as some older solc did not have 'type').
        type_name_key = "type" if "type" in t["attributes"] else key
        return _find_from_type_name(
            t["attributes"][type_name_key],
            functions,
            contracts,
            structures_direct_access,
            all_structures,
            enums_direct_access,
            all_enums,
        )

    if t[key] == "ArrayTypeName":
        length = None
        if is_compact_ast:
            if t.get("length", None):
                length = parse_expression(t["length"], caller_context)
            array_type = parse_type(t["baseType"], next_context)
        else:
            if len(t["children"]) == 2:
                length = parse_expression(t["children"][1], caller_context)
            else:
                assert len(t["children"]) == 1
            array_type = parse_type(t["children"][0], next_context)
        return ArrayType(array_type, length)

    if t[key] == "Mapping":

        if is_compact_ast:
            mappingFrom = parse_type(t["keyType"], next_context)
            mappingTo = parse_type(t["valueType"], next_context)
        else:
            assert len(t["children"]) == 2

            mappingFrom = parse_type(t["children"][0], next_context)
            mappingTo = parse_type(t["children"][1], next_context)

        return MappingType(mappingFrom, mappingTo)

    if t[key] == "FunctionTypeName":

        if is_compact_ast:
            params = t["parameterTypes"]
            return_values = t["returnParameterTypes"]
            index = "parameters"
        else:
            assert len(t["children"]) == 2
            params = t["children"][0]
            return_values = t["children"][1]
            index = "children"

        assert params[key] == "ParameterList"
        assert return_values[key] == "ParameterList"

        params_vars: List[FunctionTypeVariable] = []
        return_values_vars: List[FunctionTypeVariable] = []
        for p in params[index]:
            var = FunctionTypeVariable()
            var.set_offset(p["src"], caller_context.slither)

            var_parser = FunctionTypeVariableSolc(var, p)
            var_parser.analyze(caller_context)

            params_vars.append(var)
        for p in return_values[index]:
            var = FunctionTypeVariable()
            var.set_offset(p["src"], caller_context.slither)

            var_parser = FunctionTypeVariableSolc(var, p)
            var_parser.analyze(caller_context)

            return_values_vars.append(var)

        return FunctionType(params_vars, return_values_vars)

    raise ParsingError("Type name not found " + str(t))
Exemplo n.º 14
0
 def set_type(self, t):
     if isinstance(t, str):
         t = ElementaryType(t)
     assert isinstance(t, (Type, list)) or t is None
     self._type = t
def test_functions():
    # pylint: disable=too-many-statements
    slither = Slither("tests/test_function.sol")
    compilation_unit = slither.compilation_units[0]
    functions = compilation_unit.get_contract_from_name(
        "TestFunction")[0].available_functions_as_dict()

    f = functions["external_payable(uint256)"]
    assert f.name == "external_payable"
    assert f.full_name == "external_payable(uint256)"
    assert f.canonical_name == "TestFunction.external_payable(uint256)"
    assert f.solidity_signature == "external_payable(uint256)"
    assert f.signature_str == "external_payable(uint256) returns(uint256)"
    assert f.function_type == FunctionType.NORMAL
    assert f.contains_assembly is False
    assert f.can_reenter() is False
    assert f.can_send_eth() is False
    assert f.is_constructor is False
    assert f.is_fallback is False
    assert f.is_receive is False
    assert f.payable is True
    assert f.visibility == "external"
    assert f.view is False
    assert f.pure is False
    assert f.is_implemented is True
    assert f.is_empty is False
    assert f.parameters[0].name == "_a"
    assert f.parameters[0].type == ElementaryType("uint256")
    assert f.return_type[0] == ElementaryType("uint256")

    f = functions["public_reenter()"]
    assert f.name == "public_reenter"
    assert f.full_name == "public_reenter()"
    assert f.canonical_name == "TestFunction.public_reenter()"
    assert f.solidity_signature == "public_reenter()"
    assert f.signature_str == "public_reenter() returns()"
    assert f.function_type == FunctionType.NORMAL
    assert f.contains_assembly is False
    assert f.can_reenter() is True
    assert f.can_send_eth() is False
    assert f.is_constructor is False
    assert f.is_fallback is False
    assert f.is_receive is False
    assert f.payable is False
    assert f.visibility == "public"
    assert f.view is False
    assert f.pure is False
    assert f.is_implemented is True
    assert f.is_empty is False
    assert f.parameters == []
    assert f.return_type is None

    f = functions["public_payable_reenter_send(bool)"]
    assert f.name == "public_payable_reenter_send"
    assert f.full_name == "public_payable_reenter_send(bool)"
    assert f.canonical_name == "TestFunction.public_payable_reenter_send(bool)"
    assert f.solidity_signature == "public_payable_reenter_send(bool)"
    assert f.signature_str == "public_payable_reenter_send(bool) returns()"
    assert f.function_type == FunctionType.NORMAL
    assert f.contains_assembly is False
    assert f.can_reenter() is True
    assert f.can_send_eth() is True
    assert f.is_constructor is False
    assert f.is_fallback is False
    assert f.is_receive is False
    assert f.payable is True
    assert f.visibility == "public"
    assert f.view is False
    assert f.pure is False
    assert f.is_implemented is True
    assert f.is_empty is False
    assert f.parameters[0].name == "_b"
    assert f.parameters[0].type == ElementaryType("bool")
    assert f.return_type is None

    f = functions["external_send(uint8)"]
    assert f.name == "external_send"
    assert f.full_name == "external_send(uint8)"
    assert f.canonical_name == "TestFunction.external_send(uint8)"
    assert f.solidity_signature == "external_send(uint8)"
    assert f.signature_str == "external_send(uint8) returns()"
    assert f.function_type == FunctionType.NORMAL
    assert f.contains_assembly is False
    assert f.can_reenter() is True
    assert f.can_send_eth() is True
    assert f.is_constructor is False
    assert f.is_fallback is False
    assert f.is_receive is False
    assert f.payable is False
    assert f.visibility == "external"
    assert f.view is False
    assert f.pure is False
    assert f.is_implemented is True
    assert f.is_empty is False
    assert f.parameters[0].name == "_c"
    assert f.parameters[0].type == ElementaryType("uint8")
    assert f.return_type is None

    f = functions["internal_assembly(bytes)"]
    assert f.name == "internal_assembly"
    assert f.full_name == "internal_assembly(bytes)"
    assert f.canonical_name == "TestFunction.internal_assembly(bytes)"
    assert f.solidity_signature == "internal_assembly(bytes)"
    assert f.signature_str == "internal_assembly(bytes) returns(uint256)"
    assert f.function_type == FunctionType.NORMAL
    assert f.contains_assembly is True
    assert f.can_reenter() is False
    assert f.can_send_eth() is False
    assert f.is_constructor is False
    assert f.is_fallback is False
    assert f.is_receive is False
    assert f.payable is False
    assert f.visibility == "internal"
    assert f.view is False
    assert f.pure is False
    assert f.is_implemented is True
    assert f.is_empty is False
    assert f.parameters[0].name == "_d"
    assert f.parameters[0].type == ElementaryType("bytes")
    assert f.return_type[0] == ElementaryType("uint256")

    f = functions["fallback()"]
    assert f.name == "fallback"
    assert f.full_name == "fallback()"
    assert f.canonical_name == "TestFunction.fallback()"
    assert f.solidity_signature == "fallback()"
    assert f.signature_str == "fallback() returns()"
    assert f.function_type == FunctionType.FALLBACK
    assert f.contains_assembly is False
    assert f.can_reenter() is False
    assert f.can_send_eth() is False
    assert f.is_constructor is False
    assert f.is_fallback is True
    assert f.is_receive is False
    assert f.payable is False
    assert f.visibility == "external"
    assert f.view is False
    assert f.pure is False
    assert f.is_implemented is True
    assert f.is_empty is True
    assert f.parameters == []
    assert f.return_type is None

    f = functions["receive()"]
    assert f.name == "receive"
    assert f.full_name == "receive()"
    assert f.canonical_name == "TestFunction.receive()"
    assert f.solidity_signature == "receive()"
    assert f.signature_str == "receive() returns()"
    assert f.function_type == FunctionType.RECEIVE
    assert f.contains_assembly is False
    assert f.can_reenter() is False
    assert f.can_send_eth() is False
    assert f.is_constructor is False
    assert f.is_fallback is False
    assert f.is_receive is True
    assert f.payable is True
    assert f.visibility == "external"
    assert f.view is False
    assert f.pure is False
    assert f.is_implemented is True
    assert f.is_empty is True
    assert f.parameters == []
    assert f.return_type is None

    f = functions["constructor(address)"]
    assert f.name == "constructor"
    assert f.full_name == "constructor(address)"
    assert f.canonical_name == "TestFunction.constructor(address)"
    assert f.solidity_signature == "constructor(address)"
    assert f.signature_str == "constructor(address) returns()"
    assert f.function_type == FunctionType.CONSTRUCTOR
    assert f.contains_assembly is False
    assert f.can_reenter() is False
    assert f.can_send_eth() is False
    assert f.is_constructor
    assert f.is_fallback is False
    assert f.is_receive is False
    assert f.payable is True
    assert f.visibility == "public"
    assert f.view is False
    assert f.pure is False
    assert f.is_implemented is True
    assert f.is_empty is True
    assert f.parameters[0].name == "_e"
    assert f.parameters[0].type == ElementaryType("address")
    assert f.return_type is None

    f = functions["private_view()"]
    assert f.name == "private_view"
    assert f.full_name == "private_view()"
    assert f.canonical_name == "TestFunction.private_view()"
    assert f.solidity_signature == "private_view()"
    assert f.signature_str == "private_view() returns(bool)"
    assert f.function_type == FunctionType.NORMAL
    assert f.contains_assembly is False
    assert f.can_reenter() is False
    assert f.can_send_eth() is False
    assert f.is_constructor is False
    assert f.is_fallback is False
    assert f.is_receive is False
    assert f.payable is False
    assert f.visibility == "private"
    assert f.view is True
    assert f.pure is False
    assert f.is_implemented is True
    assert f.is_empty is False
    assert f.parameters == []
    assert f.return_type[0] == ElementaryType("bool")

    f = functions["public_pure()"]
    assert f.name == "public_pure"
    assert f.full_name == "public_pure()"
    assert f.canonical_name == "TestFunction.public_pure()"
    assert f.solidity_signature == "public_pure()"
    assert f.signature_str == "public_pure() returns(bool)"
    assert f.function_type == FunctionType.NORMAL
    assert f.contains_assembly is False
    assert f.can_reenter() is False
    assert f.can_send_eth() is False
    assert f.is_constructor is False
    assert f.is_fallback is False
    assert f.is_receive is False
    assert f.payable is False
    assert f.visibility == "public"
    assert f.view is True
    assert f.pure is True
    assert f.is_implemented is True
    assert f.is_empty is False
    assert f.parameters == []
    assert f.return_type[0] == ElementaryType("bool")
Exemplo n.º 16
0
def parse_type(t: Union[Dict, UnknownType], caller_context):
    # local import to avoid circular dependency
    # pylint: disable=too-many-locals,too-many-branches,too-many-statements
    # pylint: disable=import-outside-toplevel
    from slither.solc_parsing.expressions.expression_parsing import parse_expression
    from slither.solc_parsing.variables.function_type_variable import FunctionTypeVariableSolc
    from slither.solc_parsing.declarations.contract import ContractSolc
    from slither.solc_parsing.declarations.function import FunctionSolc

    if isinstance(caller_context, ContractSolc):
        contract = caller_context.underlying_contract
        contract_parser = caller_context
        is_compact_ast = caller_context.is_compact_ast
    elif isinstance(caller_context, FunctionSolc):
        contract = caller_context.underlying_function.contract
        contract_parser = caller_context.contract_parser
        is_compact_ast = caller_context.is_compact_ast
    else:
        raise ParsingError(f"Incorrect caller context: {type(caller_context)}")

    if is_compact_ast:
        key = "nodeType"
    else:
        key = "name"

    structures = contract.structures + contract.slither.top_level_structures
    enums = contract.enums + contract.slither.top_level_enums
    contracts = contract.slither.contracts

    if isinstance(t, UnknownType):
        return _find_from_type_name(t.name, contract, contracts, structures, enums)

    if t[key] == "ElementaryTypeName":
        if is_compact_ast:
            return ElementaryType(t["name"])
        return ElementaryType(t["attributes"][key])

    if t[key] == "UserDefinedTypeName":
        if is_compact_ast:
            return _find_from_type_name(
                t["typeDescriptions"]["typeString"],
                contract,
                contracts,
                structures,
                enums,
            )

        # Determine if we have a type node (otherwise we use the name node, as some older solc did not have 'type').
        type_name_key = "type" if "type" in t["attributes"] else key
        return _find_from_type_name(
            t["attributes"][type_name_key], contract, contracts, structures, enums
        )

    if t[key] == "ArrayTypeName":
        length = None
        if is_compact_ast:
            if t.get("length", None):
                length = parse_expression(t["length"], caller_context)
            array_type = parse_type(t["baseType"], contract_parser)
        else:
            if len(t["children"]) == 2:
                length = parse_expression(t["children"][1], caller_context)
            else:
                assert len(t["children"]) == 1
            array_type = parse_type(t["children"][0], contract_parser)
        return ArrayType(array_type, length)

    if t[key] == "Mapping":

        if is_compact_ast:
            mappingFrom = parse_type(t["keyType"], contract_parser)
            mappingTo = parse_type(t["valueType"], contract_parser)
        else:
            assert len(t["children"]) == 2

            mappingFrom = parse_type(t["children"][0], contract_parser)
            mappingTo = parse_type(t["children"][1], contract_parser)

        return MappingType(mappingFrom, mappingTo)

    if t[key] == "FunctionTypeName":

        if is_compact_ast:
            params = t["parameterTypes"]
            return_values = t["returnParameterTypes"]
            index = "parameters"
        else:
            assert len(t["children"]) == 2
            params = t["children"][0]
            return_values = t["children"][1]
            index = "children"

        assert params[key] == "ParameterList"
        assert return_values[key] == "ParameterList"

        params_vars: List[FunctionTypeVariable] = []
        return_values_vars: List[FunctionTypeVariable] = []
        for p in params[index]:
            var = FunctionTypeVariable()
            var.set_offset(p["src"], caller_context.slither)

            var_parser = FunctionTypeVariableSolc(var, p)
            var_parser.analyze(caller_context)

            params_vars.append(var)
        for p in return_values[index]:
            var = FunctionTypeVariable()
            var.set_offset(p["src"], caller_context.slither)

            var_parser = FunctionTypeVariableSolc(var, p)
            var_parser.analyze(caller_context)

            return_values_vars.append(var)

        return FunctionType(params_vars, return_values_vars)

    raise ParsingError("Type name not found " + str(t))
Exemplo n.º 17
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['name']

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

        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':
        attributes = expression['attributes']
        operation_type = BinaryOperationType.get_type(attributes['operator'])

        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 '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':
        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)
        #print(conditional)
        return conditional

    elif name == 'Assignment':
        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
        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
        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':
        index_type = expression['attributes']['type']
        children = expression['children']
        assert len(children) == 2
        left_expression = parse_expression(children[0], caller_context)
        right_expression = parse_expression(children[1], caller_context)
        index = IndexAccess(left_expression, right_expression, index_type)
        return index

    elif name == 'MemberAccess':
        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)
            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;
        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':
        new_type = expression['attributes']['type']

        children = expression['children']
        assert len(children) == 1
        #new_expression = parse_expression(children[0])

        child = children[0]

        if child['name'] == 'ArrayTypeName':
            depth = 0
            while child['name'] == 'ArrayTypeName':
                # Note: dont conserve the size of the array if provided
                #assert len(child['children']) == 1
                child = child['children'][0]
                depth += 1

            if child['name'] == 'ElementaryTypeName':
                array_type = ElementaryType(child['attributes']['name'])
            elif child['name'] == 'UserDefinedTypeName':
                array_type = parse_type(
                    UnknownType(child['attributes']['name']), caller_context)
            else:
                logger.error('Incorrect type array {}'.format(child))
                exit(-1)
            array = NewArray(depth, array_type)
            return array

        if child['name'] == 'ElementaryTypeName':
            elem_type = ElementaryType(child['attributes']['name'])
            new_elem = NewElementaryType(elem_type)
            return new_elem

        assert child['name'] == 'UserDefinedTypeName'

        contract_name = child['attributes']['name']
        new = NewContract(contract_name)
        return new

    elif name == 'ModifierInvocation':

        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)
Exemplo n.º 18
0
def parse_type(t, caller_context):
    # local import to avoid circular dependency
    from slither.solc_parsing.expressions.expression_parsing import parse_expression
    from slither.solc_parsing.variables.function_type_variable import FunctionTypeVariableSolc

    if isinstance(caller_context, Contract):
        contract = caller_context
    elif isinstance(caller_context, Function):
        contract = caller_context.contract
    else:
        logger.error('Incorrect caller context')
        exit(-1)

    structures = contract.structures
    enums = contract.enums
    contracts = contract.slither.contracts

    if isinstance(t, UnknownType):
        return _find_from_type_name(t.name, contract, contracts, structures,
                                    enums)

    elif t['name'] == 'ElementaryTypeName':
        return ElementaryType(t['attributes']['name'])

    elif t['name'] == 'UserDefinedTypeName':
        return _find_from_type_name(t['attributes']['name'], contract,
                                    contracts, structures, enums)

    elif t['name'] == 'ArrayTypeName':
        length = None
        if len(t['children']) == 2:
            length = parse_expression(t['children'][1], caller_context)
        else:
            assert len(t['children']) == 1
        array_type = parse_type(t['children'][0], contract)
        return ArrayType(array_type, length)

    elif t['name'] == 'Mapping':

        assert len(t['children']) == 2

        mappingFrom = parse_type(t['children'][0], contract)
        mappingTo = parse_type(t['children'][1], contract)

        return MappingType(mappingFrom, mappingTo)

    elif t['name'] == 'FunctionTypeName':
        assert len(t['children']) == 2

        params = t['children'][0]
        return_values = t['children'][1]

        assert params['name'] == 'ParameterList'
        assert return_values['name'] == 'ParameterList'

        params_vars = []
        return_values_vars = []
        for p in params['children']:
            var = FunctionTypeVariableSolc(p)
            var.set_offset(p['src'], caller_context.slither)
            var.analyze(caller_context)
            params_vars.append(var)
        for p in return_values['children']:
            var = FunctionTypeVariableSolc(p)

            var.set_offset(p['src'], caller_context.slither)
            var.analyze(caller_context)
            return_values_vars.append(var)

        return FunctionType(params_vars, return_values_vars)

    logger.error('Type name not found ' + str(t))
    exit(-1)
def parse_type(
    t: Union[Dict, UnknownType],
    caller_context: Union[CallerContextExpression,
                          "SlitherCompilationUnitSolc"],
) -> Type:
    """
    caller_context can be a SlitherCompilationUnitSolc because we recursively call the function
    and go up in the context's scope. If we are really lost we just go over the SlitherCompilationUnitSolc

    :param t:
    :type t:
    :param caller_context:
    :type caller_context:
    :return:
    :rtype:
    """
    # local import to avoid circular dependency
    # pylint: disable=too-many-locals,too-many-branches,too-many-statements
    # pylint: disable=import-outside-toplevel
    from slither.solc_parsing.expressions.expression_parsing import parse_expression
    from slither.solc_parsing.variables.function_type_variable import FunctionTypeVariableSolc
    from slither.solc_parsing.declarations.contract import ContractSolc
    from slither.solc_parsing.declarations.function import FunctionSolc
    from slither.solc_parsing.declarations.custom_error import CustomErrorSolc
    from slither.solc_parsing.declarations.structure_top_level import StructureTopLevelSolc
    from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
    from slither.solc_parsing.variables.top_level_variable import TopLevelVariableSolc

    sl: "SlitherCompilationUnit"
    renaming: Dict[str, str]
    user_defined_types: Dict[str, TypeAlias]
    # Note: for convenicence top level functions use the same parser than function in contract
    # but contract_parser is set to None
    if isinstance(caller_context, SlitherCompilationUnitSolc) or (
            isinstance(caller_context, FunctionSolc)
            and caller_context.contract_parser is None):
        structures_direct_access: List["Structure"]
        if isinstance(caller_context, SlitherCompilationUnitSolc):
            sl = caller_context.compilation_unit
            next_context = caller_context
            renaming = {}
            user_defined_types = {}
        else:
            assert isinstance(caller_context, FunctionSolc)
            sl = caller_context.underlying_function.compilation_unit
            next_context = caller_context.slither_parser
            renaming = caller_context.underlying_function.file_scope.renaming
            user_defined_types = caller_context.underlying_function.file_scope.user_defined_types
        structures_direct_access = sl.structures_top_level
        all_structuress = [c.structures for c in sl.contracts]
        all_structures = [
            item for sublist in all_structuress for item in sublist
        ]
        all_structures += structures_direct_access
        enums_direct_access = sl.enums_top_level
        all_enumss = [c.enums for c in sl.contracts]
        all_enums = [item for sublist in all_enumss for item in sublist]
        all_enums += enums_direct_access
        contracts = sl.contracts
        functions = []
    elif isinstance(
            caller_context,
        (StructureTopLevelSolc, CustomErrorSolc, TopLevelVariableSolc)):
        if isinstance(caller_context, StructureTopLevelSolc):
            scope = caller_context.underlying_structure.file_scope
        elif isinstance(caller_context, TopLevelVariableSolc):
            scope = caller_context.underlying_variable.file_scope
        else:
            assert isinstance(caller_context, CustomErrorSolc)
            custom_error = caller_context.underlying_custom_error
            if isinstance(custom_error, CustomErrorTopLevel):
                scope = custom_error.file_scope
            else:
                assert isinstance(custom_error, CustomErrorContract)
                scope = custom_error.contract.file_scope

        next_context = caller_context.slither_parser
        structures_direct_access = list(scope.structures.values())
        all_structuress = [c.structures for c in scope.contracts.values()]
        all_structures = [
            item for sublist in all_structuress for item in sublist
        ]
        all_structures += structures_direct_access
        enums_direct_access = []
        all_enums = scope.enums.values()
        contracts = scope.contracts.values()
        functions = list(scope.functions)

        renaming = scope.renaming
        user_defined_types = scope.user_defined_types
    elif isinstance(caller_context, (ContractSolc, FunctionSolc)):
        if isinstance(caller_context, FunctionSolc):
            underlying_func = caller_context.underlying_function
            # If contract_parser is set to None, then underlying_function is a functionContract
            # See note above
            assert isinstance(underlying_func, FunctionContract)
            contract = underlying_func.contract
            next_context = caller_context.contract_parser
            scope = caller_context.underlying_function.file_scope
        else:
            contract = caller_context.underlying_contract
            next_context = caller_context
            scope = caller_context.underlying_contract.file_scope

        structures_direct_access = contract.structures
        structures_direct_access += contract.file_scope.structures.values()
        all_structuress = [
            c.structures for c in contract.file_scope.contracts.values()
        ]
        all_structures = [
            item for sublist in all_structuress for item in sublist
        ]
        all_structures += contract.file_scope.structures.values()
        enums_direct_access: List["Enum"] = contract.enums
        enums_direct_access += contract.file_scope.enums.values()
        all_enumss = [c.enums for c in contract.file_scope.contracts.values()]
        all_enums = [item for sublist in all_enumss for item in sublist]
        all_enums += contract.file_scope.enums.values()
        contracts = contract.file_scope.contracts.values()
        functions = contract.functions + contract.modifiers

        renaming = scope.renaming
        user_defined_types = scope.user_defined_types
    else:
        raise ParsingError(f"Incorrect caller context: {type(caller_context)}")

    is_compact_ast = caller_context.is_compact_ast
    if is_compact_ast:
        key = "nodeType"
    else:
        key = "name"

    if isinstance(t, UnknownType):
        name = t.name
        if name in renaming:
            name = renaming[name]
        if name in user_defined_types:
            return user_defined_types[name]
        return _find_from_type_name(
            name,
            functions,
            contracts,
            structures_direct_access,
            all_structures,
            enums_direct_access,
            all_enums,
        )

    if t[key] == "ElementaryTypeName":
        if is_compact_ast:
            return ElementaryType(t["name"])
        return ElementaryType(t["attributes"][key])

    if t[key] == "UserDefinedTypeName":
        if is_compact_ast:
            name = t["typeDescriptions"]["typeString"]
            if name in renaming:
                name = renaming[name]
            if name in user_defined_types:
                return user_defined_types[name]
            return _find_from_type_name(
                name,
                functions,
                contracts,
                structures_direct_access,
                all_structures,
                enums_direct_access,
                all_enums,
            )

        # Determine if we have a type node (otherwise we use the name node, as some older solc did not have 'type').
        type_name_key = "type" if "type" in t["attributes"] else key

        name = t["attributes"][type_name_key]
        if name in renaming:
            name = renaming[name]
        if name in user_defined_types:
            return user_defined_types[name]
        return _find_from_type_name(
            name,
            functions,
            contracts,
            structures_direct_access,
            all_structures,
            enums_direct_access,
            all_enums,
        )

    # Introduced with Solidity 0.8
    if t[key] == "IdentifierPath":
        if is_compact_ast:
            name = t["name"]
            if name in renaming:
                name = renaming[name]
            if name in user_defined_types:
                return user_defined_types[name]
            return _find_from_type_name(
                name,
                functions,
                contracts,
                structures_direct_access,
                all_structures,
                enums_direct_access,
                all_enums,
            )

        raise SlitherError("Solidity 0.8 not supported with the legacy AST")

    if t[key] == "ArrayTypeName":
        length = None
        if is_compact_ast:
            if t.get("length", None):
                length = parse_expression(t["length"], caller_context)
            array_type = parse_type(t["baseType"], next_context)
        else:
            if len(t["children"]) == 2:
                length = parse_expression(t["children"][1], caller_context)
            else:
                assert len(t["children"]) == 1
            array_type = parse_type(t["children"][0], next_context)
        return ArrayType(array_type, length)

    if t[key] == "Mapping":

        if is_compact_ast:
            mappingFrom = parse_type(t["keyType"], next_context)
            mappingTo = parse_type(t["valueType"], next_context)
        else:
            assert len(t["children"]) == 2

            mappingFrom = parse_type(t["children"][0], next_context)
            mappingTo = parse_type(t["children"][1], next_context)

        return MappingType(mappingFrom, mappingTo)

    if t[key] == "FunctionTypeName":

        if is_compact_ast:
            params = t["parameterTypes"]
            return_values = t["returnParameterTypes"]
            index = "parameters"
        else:
            assert len(t["children"]) == 2
            params = t["children"][0]
            return_values = t["children"][1]
            index = "children"

        assert params[key] == "ParameterList"
        assert return_values[key] == "ParameterList"

        params_vars: List[FunctionTypeVariable] = []
        return_values_vars: List[FunctionTypeVariable] = []
        for p in params[index]:
            var = FunctionTypeVariable()
            var.set_offset(p["src"], caller_context.compilation_unit)

            var_parser = FunctionTypeVariableSolc(var, p)
            var_parser.analyze(caller_context)

            params_vars.append(var)
        for p in return_values[index]:
            var = FunctionTypeVariable()
            var.set_offset(p["src"], caller_context.compilation_unit)

            var_parser = FunctionTypeVariableSolc(var, p)
            var_parser.analyze(caller_context)

            return_values_vars.append(var)

        return FunctionType(params_vars, return_values_vars)

    raise ParsingError("Type name not found " + str(t))
Exemplo n.º 20
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['name']

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

        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':
        attributes = expression['attributes']
        operation_type = BinaryOperationType.get_type(attributes['operator'])

        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':
        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']
            ]
        t = TupleExpression(expressions)
        return t

    elif name == 'Conditional':
        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)
        #print(conditional)
        return conditional

    elif name == 'Assignment':
        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
        value = expression['attributes']['value']
        literal = Literal(value)
        return literal

    elif name == 'Identifier':
        assert 'children' not in expression
        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':
        index_type = expression['attributes']['type']
        children = expression['children']
        assert len(children) == 2
        left_expression = parse_expression(children[0], caller_context)
        right_expression = parse_expression(children[1], caller_context)
        index = IndexAccess(left_expression, right_expression, index_type)
        return index

    elif name == 'MemberAccess':
        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)
            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;
        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':
        new_type = expression['attributes']['type']

        children = expression['children']
        assert len(children) == 1
        #new_expression = parse_expression(children[0])

        child = children[0]

        if child['name'] == 'ArrayTypeName':
            depth = 0
            while child['name'] == 'ArrayTypeName':
                # Note: dont conserve the size of the array if provided
                #assert len(child['children']) == 1
                child = child['children'][0]
                depth += 1

            if child['name'] == 'ElementaryTypeName':
                array_type = ElementaryType(child['attributes']['name'])
            elif child['name'] == 'UserDefinedTypeName':
                array_type = parse_type(
                    UnknownType(child['attributes']['name']), caller_context)
            else:
                logger.error('Incorrect type array {}'.format(child))
                exit(-1)
            array = NewArray(depth, array_type)
            return array

        if child['name'] == 'ElementaryTypeName':
            elem_type = ElementaryType(child['attributes']['name'])
            new_elem = NewElementaryType(elem_type)
            return new_elem

        assert child['name'] == 'UserDefinedTypeName'

        contract_name = child['attributes']['name']
        new = NewContract(contract_name)
        return new

    elif name == 'ModifierInvocation':

        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)
Exemplo n.º 21
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'))