diff --git a/slither/core/declarations/contract.py b/slither/core/declarations/contract.py index 4fc274dea..73c7fbfc5 100644 --- a/slither/core/declarations/contract.py +++ b/slither/core/declarations/contract.py @@ -11,7 +11,7 @@ from slither.core.cfg.scope import Scope from slither.core.solidity_types.type import Type from slither.core.source_mapping.source_mapping import SourceMapping -from slither.core.declarations.function import Function, FunctionType +from slither.core.declarations.function import Function, FunctionType, FunctionLanguage from slither.utils.erc import ( ERC20_signatures, ERC165_signatures, @@ -540,7 +540,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods def available_elements_from_inheritances( self, elements: Dict[str, "Function"], - getter_available: Callable[["Contract"], List["Function"]], + getter_available: Callable[["Contract"], List["FunctionContract"]], ) -> Dict[str, "Function"]: """ @@ -551,14 +551,16 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods # keep track of the contracts visited # to prevent an ovveride due to multiple inheritance of the same contract # A is B, C, D is C, --> the second C was already seen - inherited_elements: Dict[str, "Function"] = {} + inherited_elements: Dict[str, "FunctionContract"] = {} accessible_elements = {} contracts_visited = [] for father in self.inheritance_reverse: - functions: Dict[str, "Function"] = { + functions: Dict[str, "FunctionContract"] = { v.full_name: v for v in getter_available(father) if v.contract not in contracts_visited + and v.function_language + != FunctionLanguage.Yul # Yul functions are not propagated in the inheritance } contracts_visited.append(father) inherited_elements.update(functions) diff --git a/slither/core/declarations/function.py b/slither/core/declarations/function.py index feb97ac91..60ce01290 100644 --- a/slither/core/declarations/function.py +++ b/slither/core/declarations/function.py @@ -104,6 +104,12 @@ def _filter_state_variables_written(expressions: List["Expression"]): return ret +class FunctionLanguage(Enum): + Solidity = 0 + Yul = 1 + Vyper = 2 + + class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-public-methods """ Function class @@ -207,6 +213,9 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu self.compilation_unit: "SlitherCompilationUnit" = compilation_unit + # Assume we are analyzing Solidty by default + self.function_language: FunctionLanguage = FunctionLanguage.Solidity + ################################################################################### ################################################################################### # region General properties diff --git a/slither/solc_parsing/yul/parse_yul.py b/slither/solc_parsing/yul/parse_yul.py index 624e666d3..783f03761 100644 --- a/slither/solc_parsing/yul/parse_yul.py +++ b/slither/solc_parsing/yul/parse_yul.py @@ -10,7 +10,9 @@ from slither.core.declarations import ( SolidityFunction, Contract, ) +from slither.core.declarations.function import FunctionLanguage from slither.core.declarations.function_contract import FunctionContract +from slither.core.declarations.function_top_level import FunctionTopLevel from slither.core.expressions import ( Literal, AssignmentOperation, @@ -353,7 +355,13 @@ def convert_yul_block( def convert_yul_function_definition( root: YulScope, parent: YulNode, ast: Dict, node_scope: Union[Function, Scope] ) -> YulNode: - func = FunctionContract(root.compilation_unit) + top_node_scope = node_scope + while not isinstance(top_node_scope, Function): + top_node_scope = top_node_scope.father + + assert isinstance(top_node_scope, (FunctionTopLevel, FunctionContract)) + func = type(top_node_scope)(root.compilation_unit) + func.function_language = FunctionLanguage.Yul yul_function = YulFunction(func, root, ast, node_scope) root.contract.add_function(func) @@ -707,6 +715,15 @@ def parse_yul_function_call(root: YulScope, node: YulNode, ast: Dict) -> Optiona raise SlitherException(f"unexpected function call target type {str(type(ident.value))}") +def _check_for_state_variable_name(root: YulScope, potential_name: str) -> Optional[Identifier]: + root_function = root.function + if isinstance(root_function, FunctionContract): + var = root_function.contract.get_state_variable_from_name(potential_name) + if var: + return Identifier(var) + return None + + def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[Expression]: name = ast["name"] @@ -739,17 +756,17 @@ def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[ # check for magic suffixes if name.endswith("_slot") or name.endswith(".slot"): potential_name = name[:-5] - var = root.function.contract.get_state_variable_from_name(potential_name) - if var: - return Identifier(var) + variable_found = _check_for_state_variable_name(root, potential_name) + if variable_found: + return variable_found var = root.function.get_local_variable_from_name(potential_name) if var and var.is_storage: return Identifier(var) if name.endswith("_offset") or name.endswith(".offset"): potential_name = name[:-7] - var = root.function.contract.get_state_variable_from_name(potential_name) - if var: - return Identifier(var) + variable_found = _check_for_state_variable_name(root, potential_name) + if variable_found: + return variable_found raise SlitherException(f"unresolved reference to identifier {name}") diff --git a/tests/ast-parsing/compile/yul-0.8.0-compact.zip b/tests/ast-parsing/compile/yul-0.8.0-compact.zip index 09eb4702c..9f9a054ae 100644 Binary files a/tests/ast-parsing/compile/yul-0.8.0-compact.zip and b/tests/ast-parsing/compile/yul-0.8.0-compact.zip differ diff --git a/tests/ast-parsing/compile/yul-0.8.1-compact.zip b/tests/ast-parsing/compile/yul-0.8.1-compact.zip index a30c6593e..2d3c07851 100644 Binary files a/tests/ast-parsing/compile/yul-0.8.1-compact.zip and b/tests/ast-parsing/compile/yul-0.8.1-compact.zip differ diff --git a/tests/ast-parsing/compile/yul-0.8.10-compact.zip b/tests/ast-parsing/compile/yul-0.8.10-compact.zip index 90ab8f14c..b4c9d3c46 100644 Binary files a/tests/ast-parsing/compile/yul-0.8.10-compact.zip and b/tests/ast-parsing/compile/yul-0.8.10-compact.zip differ diff --git a/tests/ast-parsing/compile/yul-0.8.2-compact.zip b/tests/ast-parsing/compile/yul-0.8.2-compact.zip index bbaa04ea5..4905ea726 100644 Binary files a/tests/ast-parsing/compile/yul-0.8.2-compact.zip and b/tests/ast-parsing/compile/yul-0.8.2-compact.zip differ diff --git a/tests/ast-parsing/compile/yul-0.8.3-compact.zip b/tests/ast-parsing/compile/yul-0.8.3-compact.zip index a0bee4949..2699b20dc 100644 Binary files a/tests/ast-parsing/compile/yul-0.8.3-compact.zip and b/tests/ast-parsing/compile/yul-0.8.3-compact.zip differ diff --git a/tests/ast-parsing/compile/yul-0.8.4-compact.zip b/tests/ast-parsing/compile/yul-0.8.4-compact.zip index 1ddb1c8ed..c61f44a07 100644 Binary files a/tests/ast-parsing/compile/yul-0.8.4-compact.zip and b/tests/ast-parsing/compile/yul-0.8.4-compact.zip differ diff --git a/tests/ast-parsing/compile/yul-0.8.5-compact.zip b/tests/ast-parsing/compile/yul-0.8.5-compact.zip index 5b6b1b03b..54fa8ca25 100644 Binary files a/tests/ast-parsing/compile/yul-0.8.5-compact.zip and b/tests/ast-parsing/compile/yul-0.8.5-compact.zip differ diff --git a/tests/ast-parsing/compile/yul-0.8.6-compact.zip b/tests/ast-parsing/compile/yul-0.8.6-compact.zip index 122b839d8..ed9f2ac82 100644 Binary files a/tests/ast-parsing/compile/yul-0.8.6-compact.zip and b/tests/ast-parsing/compile/yul-0.8.6-compact.zip differ diff --git a/tests/ast-parsing/compile/yul-0.8.7-compact.zip b/tests/ast-parsing/compile/yul-0.8.7-compact.zip index 08ad8c44b..f798b2361 100644 Binary files a/tests/ast-parsing/compile/yul-0.8.7-compact.zip and b/tests/ast-parsing/compile/yul-0.8.7-compact.zip differ diff --git a/tests/ast-parsing/compile/yul-0.8.8-compact.zip b/tests/ast-parsing/compile/yul-0.8.8-compact.zip index e31b62220..f4fad22ce 100644 Binary files a/tests/ast-parsing/compile/yul-0.8.8-compact.zip and b/tests/ast-parsing/compile/yul-0.8.8-compact.zip differ diff --git a/tests/ast-parsing/compile/yul-0.8.9-compact.zip b/tests/ast-parsing/compile/yul-0.8.9-compact.zip index 6dfecbada..799eb8fa6 100644 Binary files a/tests/ast-parsing/compile/yul-0.8.9-compact.zip and b/tests/ast-parsing/compile/yul-0.8.9-compact.zip differ diff --git a/tests/ast-parsing/yul-0.8.0.sol b/tests/ast-parsing/yul-0.8.0.sol index 090b6d405..38f7a7e09 100644 --- a/tests/ast-parsing/yul-0.8.0.sol +++ b/tests/ast-parsing/yul-0.8.0.sol @@ -50,3 +50,20 @@ contract C { } } + +// function from https://docs.soliditylang.org/en/v0.8.0/assembly.html +function at(address _addr) view returns (bytes memory o_code) { + assembly { + // retrieve the size of the code, this needs assembly + let size := extcodesize(_addr) + // allocate output byte array - this could also be done without assembly + // by using o_code = new bytes(size) + o_code := mload(0x40) + // new "memory end" including padding + mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f)))) + // store length in memory + mstore(o_code, size) + // actually retrieve the code, this needs assembly + extcodecopy(_addr, add(o_code, 0x20), 0, size) + } +}