diff --git a/slither/core/declarations/solidity_variables.py b/slither/core/declarations/solidity_variables.py index df179dd40..da85e9b12 100644 --- a/slither/core/declarations/solidity_variables.py +++ b/slither/core/declarations/solidity_variables.py @@ -1,6 +1,6 @@ # https://solidity.readthedocs.io/en/v0.4.24/units-and-global-variables.html from slither.core.context.context import Context -from slither.core.solidity_types import ElementaryType +from slither.core.solidity_types import ElementaryType, TypeInformation SOLIDITY_VARIABLES = {"now":'uint256', "this":'address', @@ -57,7 +57,8 @@ SOLIDITY_FUNCTIONS = {"gasleft()":['uint256'], "abi.encodeWithSelector()":["bytes"], "abi.encodeWithSignature()":["bytes"], # abi.decode returns an a list arbitrary types - "abi.decode()":[]} + "abi.decode()":[], + "type(address)":[]} def solidity_function_signature(name): """ @@ -125,10 +126,15 @@ class SolidityVariableComposed(SolidityVariable): class SolidityFunction: + # Non standard handling of type(address). This function returns an undefined object + # The type is dynamic + # https://solidity.readthedocs.io/en/latest/units-and-global-variables.html#type-information + # As a result, we set return_type during the Ir conversion def __init__(self, name): assert name in SOLIDITY_FUNCTIONS self._name = name + self._return_type = [ElementaryType(x) for x in SOLIDITY_FUNCTIONS[self.name]] @property def name(self): @@ -140,7 +146,11 @@ class SolidityFunction: @property def return_type(self): - return [ElementaryType(x) for x in SOLIDITY_FUNCTIONS[self.name]] + return self._return_type + + @return_type.setter + def return_type(self, r): + self._return_type = r def __str__(self): return self._name diff --git a/slither/core/solidity_types/__init__.py b/slither/core/solidity_types/__init__.py index b7ec244bf..24288488a 100644 --- a/slither/core/solidity_types/__init__.py +++ b/slither/core/solidity_types/__init__.py @@ -3,3 +3,4 @@ from .elementary_type import ElementaryType from .function_type import FunctionType from .mapping_type import MappingType from .user_defined_type import UserDefinedType +from .type_information import TypeInformation \ No newline at end of file diff --git a/slither/core/solidity_types/type_information.py b/slither/core/solidity_types/type_information.py new file mode 100644 index 000000000..ee6e71ee8 --- /dev/null +++ b/slither/core/solidity_types/type_information.py @@ -0,0 +1,23 @@ +from slither.core.solidity_types.type import Type + +# Use to model the Type(X) function, which returns an undefined type +# https://solidity.readthedocs.io/en/latest/units-and-global-variables.html#type-information +class TypeInformation(Type): + def __init__(self, c): + from slither.core.declarations.contract import Contract + + assert isinstance(c, (Contract)) + super(TypeInformation, self).__init__() + self._type = c + + @property + def type(self): + return self._type + + def __str__(self): + return f'type({self.type.name})' + + def __eq__(self, other): + if not isinstance(other, TypeInformation): + return False + return self.type == other.type \ No newline at end of file diff --git a/slither/slithir/convert.py b/slither/slithir/convert.py index a1b15d5b5..13c51d193 100644 --- a/slither/slithir/convert.py +++ b/slither/slithir/convert.py @@ -6,7 +6,7 @@ from slither.core.declarations import (Contract, Enum, Event, Function, from slither.core.expressions import Identifier, Literal from slither.core.solidity_types import (ArrayType, ElementaryType, FunctionType, MappingType, - UserDefinedType) + UserDefinedType, TypeInformation) from slither.core.solidity_types.elementary_type import Int as ElementaryTypeInt from slither.core.variables.variable import Variable from slither.core.variables.state_variable import StateVariable @@ -297,6 +297,44 @@ def propagate_type_and_convert_call(result, node): idx = idx +1 return result +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.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.lvalue.set_type(ElementaryType('bytes')) + return assignment + if ir.variable_right == 'name': + assignment = Assignment(ir.lvalue, + Constant(contract.name), + ElementaryType('string')) + assignment.lvalue.set_type(ElementaryType('string')) + return assignment + + raise SlithIRError(f'type({contract.name}).{ir.variable_right} is unknown') + + def propagate_types(ir, node): # propagate the type using_for = node.function.contract.using_for @@ -398,6 +436,8 @@ def propagate_types(ir, node): ElementaryType('bytes4')) 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)): @@ -447,6 +487,8 @@ def propagate_types(ir, node): 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])