From f0439905e02b6f6c687492fb0dcdd6a4c4e35ee4 Mon Sep 17 00:00:00 2001 From: Simone Date: Thu, 25 Aug 2022 18:51:01 +0200 Subject: [PATCH 01/25] Improve using for support --- slither/core/compilation_unit.py | 6 + slither/core/declarations/__init__.py | 3 + slither/core/declarations/contract.py | 22 +++ .../core/declarations/using_for_top_level.py | 18 +++ slither/core/scope/scope.py | 5 + slither/slithir/convert.py | 29 ++-- slither/solc_parsing/declarations/contract.py | 51 ++++++- .../declarations/using_for_top_level.py | 134 ++++++++++++++++++ .../slither_compilation_unit_solc.py | 28 ++++ .../solidity_types/type_parsing.py | 8 +- 10 files changed, 291 insertions(+), 13 deletions(-) create mode 100644 slither/core/declarations/using_for_top_level.py create mode 100644 slither/solc_parsing/declarations/using_for_top_level.py diff --git a/slither/core/compilation_unit.py b/slither/core/compilation_unit.py index a2568a5de..d97b7fbf5 100644 --- a/slither/core/compilation_unit.py +++ b/slither/core/compilation_unit.py @@ -16,6 +16,7 @@ from slither.core.declarations import ( from slither.core.declarations.custom_error import CustomError from slither.core.declarations.enum_top_level import EnumTopLevel from slither.core.declarations.function_top_level import FunctionTopLevel +from slither.core.declarations.using_for_top_level import UsingForTopLevel from slither.core.declarations.structure_top_level import StructureTopLevel from slither.core.scope.scope import FileScope from slither.core.variables.state_variable import StateVariable @@ -41,6 +42,7 @@ class SlitherCompilationUnit(Context): self._enums_top_level: List[EnumTopLevel] = [] self._variables_top_level: List[TopLevelVariable] = [] self._functions_top_level: List[FunctionTopLevel] = [] + self._using_for_top_level: List[UsingForTopLevel] = [] self._pragma_directives: List[Pragma] = [] self._import_directives: List[Import] = [] self._custom_errors: List[CustomError] = [] @@ -205,6 +207,10 @@ class SlitherCompilationUnit(Context): def functions_top_level(self) -> List[FunctionTopLevel]: return self._functions_top_level + @property + def using_for_top_level(self) -> List[UsingForTopLevel]: + return self._using_for_top_level + @property def custom_errors(self) -> List[CustomError]: return self._custom_errors diff --git a/slither/core/declarations/__init__.py b/slither/core/declarations/__init__.py index 3b619c1d1..f891ad621 100644 --- a/slither/core/declarations/__init__.py +++ b/slither/core/declarations/__init__.py @@ -12,5 +12,8 @@ from .solidity_variables import ( ) from .structure import Structure from .enum_contract import EnumContract +from .enum_top_level import EnumTopLevel from .structure_contract import StructureContract +from .structure_top_level import StructureTopLevel from .function_contract import FunctionContract +from .function_top_level import FunctionTopLevel diff --git a/slither/core/declarations/contract.py b/slither/core/declarations/contract.py index 5d65d5cc5..ba738b0f8 100644 --- a/slither/core/declarations/contract.py +++ b/slither/core/declarations/contract.py @@ -78,6 +78,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods # The only str is "*" self._using_for: Dict[Union[str, Type], List[Type]] = {} + self._using_for_complete: Dict[Union[str, Type], List[Type]] = None self._kind: Optional[str] = None self._is_interface: bool = False self._is_library: bool = False @@ -259,6 +260,27 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods def using_for(self) -> Dict[Union[str, Type], List[Type]]: return self._using_for + @property + def using_for_complete(self) -> Dict[Union[str, Type], List[Type]]: + """ + Dict[Union[str, Type], List[Type]]: Dict of merged local using for directive with top level directive + """ + + def _merge_using_for(uf1, uf2): + result = {**uf1, **uf2} + for key, value in result.items(): + if key in uf1 and key in uf2: + result[key] = value + uf1[key] + return result + + if self._using_for_complete is None: + result = self.using_for + top_level_using_for = self.file_scope.usingFor + for uftl in top_level_using_for: + result = _merge_using_for(result, uftl.using_for) + self._using_for_complete = result + return self._using_for_complete + # endregion ################################################################################### ################################################################################### diff --git a/slither/core/declarations/using_for_top_level.py b/slither/core/declarations/using_for_top_level.py new file mode 100644 index 000000000..a1b43e1c1 --- /dev/null +++ b/slither/core/declarations/using_for_top_level.py @@ -0,0 +1,18 @@ +from typing import TYPE_CHECKING, List, Dict, Union + +from slither.core.solidity_types.type import Type +from slither.core.declarations.top_level import TopLevel + +if TYPE_CHECKING: + from slither.core.scope.scope import FileScope + + +class UsingForTopLevel(TopLevel): + def __init__(self, scope: "FileScope"): + super().__init__() + self._using_for: Dict[Union[str, Type], List[Type]] = {} + self.file_scope: "FileScope" = scope + + @property + def using_for(self) -> Dict[Type, List[Type]]: + return self._using_for diff --git a/slither/core/scope/scope.py b/slither/core/scope/scope.py index c6d18556e..a27483824 100644 --- a/slither/core/scope/scope.py +++ b/slither/core/scope/scope.py @@ -5,6 +5,7 @@ from slither.core.declarations import Contract, Import, Pragma from slither.core.declarations.custom_error_top_level import CustomErrorTopLevel from slither.core.declarations.enum_top_level import EnumTopLevel from slither.core.declarations.function_top_level import FunctionTopLevel +from slither.core.declarations.using_for_top_level import UsingForTopLevel from slither.core.declarations.structure_top_level import StructureTopLevel from slither.core.solidity_types import TypeAlias from slither.core.variables.top_level_variable import TopLevelVariable @@ -35,6 +36,7 @@ class FileScope: # Because we parse the function signature later on # So we simplify the logic and have the scope fields all populated self.functions: Set[FunctionTopLevel] = set() + self.usingFor: Set[UsingForTopLevel] = set() self.imports: Set[Import] = set() self.pragmas: Set[Pragma] = set() self.structures: Dict[str, StructureTopLevel] = {} @@ -72,6 +74,9 @@ class FileScope: if not new_scope.functions.issubset(self.functions): self.functions |= new_scope.functions learn_something = True + if not new_scope.usingFor.issubset(self.usingFor): + self.usingFor |= new_scope.usingFor + learn_something = True if not new_scope.imports.issubset(self.imports): self.imports |= new_scope.imports learn_something = True diff --git a/slither/slithir/convert.py b/slither/slithir/convert.py index 829a23e92..7658deb26 100644 --- a/slither/slithir/convert.py +++ b/slither/slithir/convert.py @@ -171,10 +171,10 @@ def _fits_under_integer(val: int, can_be_int: bool, can_be_uint) -> List[str]: assert can_be_int | can_be_uint while n <= 256: if can_be_uint: - if val <= 2**n - 1: + if val <= 2 ** n - 1: ret.append(f"uint{n}") if can_be_int: - if val <= (2**n) / 2 - 1: + if val <= (2 ** n) / 2 - 1: ret.append(f"int{n}") n = n + 8 return ret @@ -498,7 +498,9 @@ def propagate_types(ir, node: "Node"): # pylint: disable=too-many-locals # propagate the type node_function = node.function using_for = ( - node_function.contract.using_for if isinstance(node_function, FunctionContract) else {} + node_function.contract.using_for_complete + if isinstance(node_function, FunctionContract) + else {} ) if isinstance(ir, OperationWithLValue): # Force assignment in case of missing previous correct type @@ -879,7 +881,9 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]): # pylint: dis # } node_func = ins.node.function using_for = ( - node_func.contract.using_for if isinstance(node_func, FunctionContract) else {} + node_func.contract.using_for_complete + if isinstance(node_func, FunctionContract) + else {} ) targeted_libraries = ( @@ -892,10 +896,14 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]): # pylint: dis lib_contract_type.type, Contract ): continue - lib_contract = lib_contract_type.type - for lib_func in lib_contract.functions: - if lib_func.name == ins.ori.variable_right: - candidates.append(lib_func) + if isinstance(lib_contract_type, FunctionContract): + # Using for with list of functions, this is the function called + candidates.append(lib_contract_type) + else: + lib_contract = lib_contract_type.type + for lib_func in lib_contract.functions: + if lib_func.name == ins.ori.variable_right: + candidates.append(lib_func) if len(candidates) == 1: lib_func = candidates[0] @@ -1325,7 +1333,10 @@ def convert_to_pop(ir, node): def look_for_library(contract, ir, using_for, t): for destination in using_for[t]: - lib_contract = contract.file_scope.get_contract_from_name(str(destination)) + if isinstance(destination, FunctionContract) and destination.contract.is_library: + lib_contract = destination.contract + else: + lib_contract = contract.file_scope.get_contract_from_name(str(destination)) if lib_contract: lib_call = LibraryCall( lib_contract, diff --git a/slither/solc_parsing/declarations/contract.py b/slither/solc_parsing/declarations/contract.py index b7f938d1d..32ca0e8ab 100644 --- a/slither/solc_parsing/declarations/contract.py +++ b/slither/solc_parsing/declarations/contract.py @@ -578,17 +578,33 @@ class ContractSolc(CallerContextExpression): try: for father in self._contract.inheritance: self._contract.using_for.update(father.using_for) - if self.is_compact_ast: for using_for in self._usingForNotParsed: - lib_name = parse_type(using_for["libraryName"], self) if "typeName" in using_for and using_for["typeName"]: type_name = parse_type(using_for["typeName"], self) else: type_name = "*" if type_name not in self._contract.using_for: self._contract.using_for[type_name] = [] - self._contract.using_for[type_name].append(lib_name) + + if "libraryName" in using_for: + self._contract.using_for[type_name].append( + parse_type(using_for["libraryName"], self) + ) + else: + # We have a list of functions. A function can be topLevel or a library function + # at this point library function are yet to be parsed so we add the function name + # and add the real function later + for f in using_for["functionList"]: + function_name = f["function"]["name"] + if function_name.find(".") != -1: + # Library function + self._contract.using_for[type_name].append(function_name) + else: + # Top level function + for tl_function in self.compilation_unit.functions_top_level: + if tl_function.name == function_name: + self._contract.using_for[type_name].append(tl_function) else: for using_for in self._usingForNotParsed: children = using_for[self.get_children()] @@ -606,6 +622,35 @@ class ContractSolc(CallerContextExpression): except (VariableNotFound, KeyError) as e: self.log_incorrect_parsing(f"Missing using for {e}") + def analyze_library_function_using_for(self): + for type_name, full_names in self._contract.using_for.items(): + # If it's a string is a library function e.g. L.a + # We add the actual function and remove the string + for full_name in full_names: + if isinstance(full_name, str): + full_name_split = full_name.split(".") + # TODO this doesn't handle the case if there is an import with an alias + # e.g. MyImport.MyLib.a + if len(full_name_split) == 2: + library_name = full_name_split[0] + function_name = full_name_split[1] + # Get the library function + found = False + for c in self.compilation_unit.contracts: + if found: + break + if c.name == library_name: + for f in c.functions: + if f.name == function_name: + self._contract.using_for[type_name].append(f) + found = True + break + self._contract.using_for[type_name].remove(full_name) + else: + self.log_incorrect_parsing( + f"Expected library function instead received {full_name}" + ) + def analyze_enums(self): try: for father in self._contract.inheritance: diff --git a/slither/solc_parsing/declarations/using_for_top_level.py b/slither/solc_parsing/declarations/using_for_top_level.py new file mode 100644 index 000000000..070ed8cf7 --- /dev/null +++ b/slither/solc_parsing/declarations/using_for_top_level.py @@ -0,0 +1,134 @@ +""" + Using For Top Level module +""" +import logging +from typing import TYPE_CHECKING, Dict, Union + +from slither.core.compilation_unit import SlitherCompilationUnit +from slither.core.declarations.using_for_top_level import UsingForTopLevel +from slither.core.solidity_types import Type, TypeAliasTopLevel +from slither.core.declarations import ( + FunctionContract, + FunctionTopLevel, + StructureTopLevel, + EnumTopLevel, +) +from slither.solc_parsing.declarations.caller_context import CallerContextExpression +from slither.solc_parsing.solidity_types.type_parsing import parse_type +from slither.core.solidity_types.user_defined_type import UserDefinedType + +if TYPE_CHECKING: + from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc + +LOGGER = logging.getLogger("UsingForTopLevelSolc") + + +class UsingForTopLevelSolc(CallerContextExpression): # pylint: disable=too-few-public-methods + """ + UsingFor class + """ + + # elems = [(type, name)] + + def __init__( # pylint: disable=too-many-arguments + self, + uftl: UsingForTopLevel, + top_level_data: Dict, + slither_parser: "SlitherCompilationUnitSolc", + ): + # TODO think if save global here is useful + self._type_name = top_level_data["typeName"] + self._global = top_level_data["global"] + + if "libraryName" in top_level_data: + self._library_name = top_level_data["libraryName"] + else: + self._functions = top_level_data["functionList"] + + self._using_for = uftl + self._slither_parser = slither_parser + + def analyze(self): + type_name = parse_type(self._type_name, self) + self._using_for.using_for[type_name] = [] + + if hasattr(self, "_library_name"): + library_name = parse_type(self._library_name, self) + self._using_for.using_for[type_name].append(library_name) + self._propagate_global(type_name, library_name) + else: + for f in self._functions: + full_name_split = f["function"]["name"].split(".") + if len(full_name_split) == 1: + # Top level function + function_name = full_name_split[0] + for tl_function in self.compilation_unit.functions_top_level: + if tl_function.name == function_name: + self._using_for.using_for[type_name].append(tl_function) + self._propagate_global(type_name, tl_function) + elif len(full_name_split) == 2: + # Library function + library_name = full_name_split[0] + function_name = full_name_split[1] + found = False + for c in self.compilation_unit.contracts: + if found: + break + if c.name == library_name: + for cf in c.functions: + if cf.name == function_name: + self._using_for.using_for[type_name].append(cf) + self._propagate_global(type_name, cf) + found = True + break + else: + # probably case if there is an import with an alias we don't handle it for now + # e.g. MyImport.MyLib.a + return + + def _propagate_global( + self, type_name: Type, to_add: Union[FunctionTopLevel, FunctionContract, UserDefinedType] + ): + if self._global: + for scope in self.compilation_unit.scopes.values(): + if isinstance(type_name, TypeAliasTopLevel): + for alias in scope.user_defined_types.values(): + if alias == type_name: + scope.usingFor.add(self._using_for) + elif isinstance(type_name, UserDefinedType): + underlying = type_name.type + if isinstance(underlying, StructureTopLevel): + for struct in scope.structures.values(): + if struct == underlying: + scope.usingFor.add(self._using_for) + elif isinstance(underlying, EnumTopLevel): + for enum in scope.enums.values(): + if enum == underlying: + scope.usingFor.add(self._using_for) + else: + LOGGER.error( + f"Error propagating global {underlying} {type(underlying)} not a StructTopLevel or EnumTopLevel" + ) + else: + LOGGER.error( + f"Found {to_add} {type(to_add)} when propagating global using for {type_name} {type(type_name)}" + ) + + @property + def is_compact_ast(self) -> bool: + return self._slither_parser.is_compact_ast + + @property + def compilation_unit(self) -> SlitherCompilationUnit: + return self._slither_parser.compilation_unit + + def get_key(self) -> str: + return self._slither_parser.get_key() + + @property + def slither_parser(self) -> "SlitherCompilationUnitSolc": + return self._slither_parser + + @property + def underlying_using_for(self) -> UsingForTopLevel: + return self._using_for diff --git a/slither/solc_parsing/slither_compilation_unit_solc.py b/slither/solc_parsing/slither_compilation_unit_solc.py index 3054b4470..828229c0c 100644 --- a/slither/solc_parsing/slither_compilation_unit_solc.py +++ b/slither/solc_parsing/slither_compilation_unit_solc.py @@ -14,6 +14,7 @@ from slither.core.declarations.function_top_level import FunctionTopLevel from slither.core.declarations.import_directive import Import from slither.core.declarations.pragma_directive import Pragma from slither.core.declarations.structure_top_level import StructureTopLevel +from slither.core.declarations.using_for_top_level import UsingForTopLevel from slither.core.scope.scope import FileScope from slither.core.solidity_types import ElementaryType, TypeAliasTopLevel from slither.core.variables.top_level_variable import TopLevelVariable @@ -22,6 +23,7 @@ from slither.solc_parsing.declarations.contract import ContractSolc from slither.solc_parsing.declarations.custom_error import CustomErrorSolc from slither.solc_parsing.declarations.function import FunctionSolc from slither.solc_parsing.declarations.structure_top_level import StructureTopLevelSolc +from slither.solc_parsing.declarations.using_for_top_level import UsingForTopLevelSolc from slither.solc_parsing.exceptions import VariableNotFound from slither.solc_parsing.variables.top_level_variable import TopLevelVariableSolc @@ -71,6 +73,7 @@ class SlitherCompilationUnitSolc: self._custom_error_parser: List[CustomErrorSolc] = [] self._variables_top_level_parser: List[TopLevelVariableSolc] = [] self._functions_top_level_parser: List[FunctionSolc] = [] + self._using_for_top_level_parser: List[UsingForTopLevelSolc] = [] self._is_compact_ast = False # self._core: SlitherCore = core @@ -221,6 +224,17 @@ class SlitherCompilationUnitSolc: scope.pragmas.add(pragma) pragma.set_offset(top_level_data["src"], self._compilation_unit) self._compilation_unit.pragma_directives.append(pragma) + + elif top_level_data[self.get_key()] == "UsingForDirective": + scope = self.compilation_unit.get_scope(filename) + usingFor = UsingForTopLevel(scope) + usingFor_parser = UsingForTopLevelSolc(usingFor, top_level_data, self) + usingFor.set_offset(top_level_data["src"], self._compilation_unit) + scope.usingFor.add(usingFor) + + self._compilation_unit.using_for_top_level.append(usingFor) + self._using_for_top_level_parser.append(usingFor_parser) + elif top_level_data[self.get_key()] == "ImportDirective": if self.is_compact_ast: import_directive = Import( @@ -495,6 +509,12 @@ Please rename it, this name is reserved for Slither's internals""" # Then we analyse state variables, functions and modifiers self._analyze_third_part(contracts_to_be_analyzed, libraries) + self._analyze_top_level_using_for() + + # Convert library function (at the moment are string) in using for that specifies list of functions + # to actual function + self._analyze_library_function_using_for(contracts_to_be_analyzed) + self._parsed = True def analyze_contracts(self): # pylint: disable=too-many-statements,too-many-branches @@ -605,6 +625,10 @@ Please rename it, this name is reserved for Slither's internals""" else: contracts_to_be_analyzed += [contract] + def _analyze_library_function_using_for(self, contracts_to_be_analyzed: List[ContractSolc]): + for c in contracts_to_be_analyzed: + c.analyze_library_function_using_for() + def _analyze_enums(self, contract: ContractSolc): # Enum must be analyzed first contract.analyze_enums() @@ -651,6 +675,10 @@ Please rename it, this name is reserved for Slither's internals""" func_parser.analyze_params() self._compilation_unit.add_function(func_parser.underlying_function) + def _analyze_top_level_using_for(self): + for using_for in self._using_for_top_level_parser: + using_for.analyze() + def _analyze_params_custom_error(self): for custom_error_parser in self._custom_error_parser: custom_error_parser.analyze_params() diff --git a/slither/solc_parsing/solidity_types/type_parsing.py b/slither/solc_parsing/solidity_types/type_parsing.py index 9a8ef5db2..b62f908d1 100644 --- a/slither/solc_parsing/solidity_types/type_parsing.py +++ b/slither/solc_parsing/solidity_types/type_parsing.py @@ -224,6 +224,7 @@ def parse_type( 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.using_for_top_level import UsingForTopLevelSolc 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 @@ -259,11 +260,16 @@ def parse_type( all_enums += enums_direct_access contracts = sl.contracts functions = [] - elif isinstance(caller_context, (StructureTopLevelSolc, CustomErrorSolc, TopLevelVariableSolc)): + elif isinstance( + caller_context, + (StructureTopLevelSolc, CustomErrorSolc, TopLevelVariableSolc, UsingForTopLevelSolc), + ): if isinstance(caller_context, StructureTopLevelSolc): scope = caller_context.underlying_structure.file_scope elif isinstance(caller_context, TopLevelVariableSolc): scope = caller_context.underlying_variable.file_scope + elif isinstance(caller_context, UsingForTopLevelSolc): + scope = caller_context.underlying_using_for.file_scope else: assert isinstance(caller_context, CustomErrorSolc) custom_error = caller_context.underlying_custom_error From 90520c591bfa0011c863094075e552e2db11e3e9 Mon Sep 17 00:00:00 2001 From: Simone Date: Fri, 26 Aug 2022 18:43:50 +0200 Subject: [PATCH 02/25] Add parsing tests --- .../using-for-1-0.8.0.sol-0.8.15-compact.zip | Bin 0 -> 4263 bytes .../using-for-2-0.8.0.sol-0.8.15-compact.zip | Bin 0 -> 4257 bytes ...ctions-list-1-0.8.0.sol-0.8.15-compact.zip | Bin 0 -> 4195 bytes ...ctions-list-2-0.8.0.sol-0.8.15-compact.zip | Bin 0 -> 3951 bytes ...ctions-list-3-0.8.0.sol-0.8.15-compact.zip | Bin 0 -> 3940 bytes ...ctions-list-4-0.8.0.sol-0.8.15-compact.zip | Bin 0 -> 4337 bytes ...ng-for-global-0.8.0.sol-0.8.15-compact.zip | Bin 0 -> 4737 bytes .../using-for-1-0.8.0.sol-0.8.15-compact.json | 10 +++++ .../using-for-2-0.8.0.sol-0.8.15-compact.json | 10 +++++ ...tions-list-1-0.8.0.sol-0.8.15-compact.json | 10 +++++ ...tions-list-2-0.8.0.sol-0.8.15-compact.json | 10 +++++ ...tions-list-3-0.8.0.sol-0.8.15-compact.json | 10 +++++ ...tions-list-4-0.8.0.sol-0.8.15-compact.json | 10 +++++ ...g-for-global-0.8.0.sol-0.8.15-compact.json | 11 +++++ tests/ast-parsing/using-for-1-0.8.0.sol | 40 +++++++++++++++++ tests/ast-parsing/using-for-2-0.8.0.sol | 40 +++++++++++++++++ .../using-for-functions-list-1-0.8.0.sol | 37 ++++++++++++++++ .../using-for-functions-list-2-0.8.0.sol | 41 ++++++++++++++++++ .../using-for-functions-list-3-0.8.0.sol | 41 ++++++++++++++++++ .../using-for-functions-list-4-0.8.0.sol | 40 +++++++++++++++++ tests/ast-parsing/using-for-global-0.8.0.sol | 15 +++++++ tests/ast-parsing/using-for-library-0.8.0.sol | 32 ++++++++++++++ tests/test_ast_parsing.py | 7 +++ 23 files changed, 364 insertions(+) create mode 100644 tests/ast-parsing/compile/using-for-1-0.8.0.sol-0.8.15-compact.zip create mode 100644 tests/ast-parsing/compile/using-for-2-0.8.0.sol-0.8.15-compact.zip create mode 100644 tests/ast-parsing/compile/using-for-functions-list-1-0.8.0.sol-0.8.15-compact.zip create mode 100644 tests/ast-parsing/compile/using-for-functions-list-2-0.8.0.sol-0.8.15-compact.zip create mode 100644 tests/ast-parsing/compile/using-for-functions-list-3-0.8.0.sol-0.8.15-compact.zip create mode 100644 tests/ast-parsing/compile/using-for-functions-list-4-0.8.0.sol-0.8.15-compact.zip create mode 100644 tests/ast-parsing/compile/using-for-global-0.8.0.sol-0.8.15-compact.zip create mode 100644 tests/ast-parsing/expected/using-for-1-0.8.0.sol-0.8.15-compact.json create mode 100644 tests/ast-parsing/expected/using-for-2-0.8.0.sol-0.8.15-compact.json create mode 100644 tests/ast-parsing/expected/using-for-functions-list-1-0.8.0.sol-0.8.15-compact.json create mode 100644 tests/ast-parsing/expected/using-for-functions-list-2-0.8.0.sol-0.8.15-compact.json create mode 100644 tests/ast-parsing/expected/using-for-functions-list-3-0.8.0.sol-0.8.15-compact.json create mode 100644 tests/ast-parsing/expected/using-for-functions-list-4-0.8.0.sol-0.8.15-compact.json create mode 100644 tests/ast-parsing/expected/using-for-global-0.8.0.sol-0.8.15-compact.json create mode 100644 tests/ast-parsing/using-for-1-0.8.0.sol create mode 100644 tests/ast-parsing/using-for-2-0.8.0.sol create mode 100644 tests/ast-parsing/using-for-functions-list-1-0.8.0.sol create mode 100644 tests/ast-parsing/using-for-functions-list-2-0.8.0.sol create mode 100644 tests/ast-parsing/using-for-functions-list-3-0.8.0.sol create mode 100644 tests/ast-parsing/using-for-functions-list-4-0.8.0.sol create mode 100644 tests/ast-parsing/using-for-global-0.8.0.sol create mode 100644 tests/ast-parsing/using-for-library-0.8.0.sol diff --git a/tests/ast-parsing/compile/using-for-1-0.8.0.sol-0.8.15-compact.zip b/tests/ast-parsing/compile/using-for-1-0.8.0.sol-0.8.15-compact.zip new file mode 100644 index 0000000000000000000000000000000000000000..6505c61e0b731ee77658aaf1120b9365b59fe246 GIT binary patch literal 4263 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 literal 0 HcmV?d00001 diff --git a/tests/ast-parsing/compile/using-for-functions-list-1-0.8.0.sol-0.8.15-compact.zip b/tests/ast-parsing/compile/using-for-functions-list-1-0.8.0.sol-0.8.15-compact.zip new file mode 100644 index 0000000000000000000000000000000000000000..6a6064a153327e35ea1dec07add44437261d7480 GIT binary patch literal 4195 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 d7~X&8e%V*UFRKB7{{zLZbMXKG literal 0 HcmV?d00001 diff --git a/tests/ast-parsing/compile/using-for-functions-list-3-0.8.0.sol-0.8.15-compact.zip b/tests/ast-parsing/compile/using-for-functions-list-3-0.8.0.sol-0.8.15-compact.zip new file mode 100644 index 0000000000000000000000000000000000000000..3944ab586fd17cefa7ccbd4fa0d437f68e8f8e5c GIT binary patch literal 3940 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 og{_41e+!}iIHCVdIM#ph|I1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n", + "b(Data,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n", + "c(Data,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n" + }, + "C": { + "libCall(uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/using-for-2-0.8.0.sol-0.8.15-compact.json b/tests/ast-parsing/expected/using-for-2-0.8.0.sol-0.8.15-compact.json new file mode 100644 index 000000000..01536efb9 --- /dev/null +++ b/tests/ast-parsing/expected/using-for-2-0.8.0.sol-0.8.15-compact.json @@ -0,0 +1,10 @@ +{ + "L1": { + "a(Data,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n", + "b(Data,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n", + "c(Data,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n" + }, + "C": { + "libCall(uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/using-for-functions-list-1-0.8.0.sol-0.8.15-compact.json b/tests/ast-parsing/expected/using-for-functions-list-1-0.8.0.sol-0.8.15-compact.json new file mode 100644 index 000000000..01536efb9 --- /dev/null +++ b/tests/ast-parsing/expected/using-for-functions-list-1-0.8.0.sol-0.8.15-compact.json @@ -0,0 +1,10 @@ +{ + "L1": { + "a(Data,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n", + "b(Data,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n", + "c(Data,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n" + }, + "C": { + "libCall(uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/using-for-functions-list-2-0.8.0.sol-0.8.15-compact.json b/tests/ast-parsing/expected/using-for-functions-list-2-0.8.0.sol-0.8.15-compact.json new file mode 100644 index 000000000..a41d2ba18 --- /dev/null +++ b/tests/ast-parsing/expected/using-for-functions-list-2-0.8.0.sol-0.8.15-compact.json @@ -0,0 +1,10 @@ +{ + "L1": { + "a(Data,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n", + "b(Data,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n", + "c(Data,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n" + }, + "C": { + "topLevelCall(uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/using-for-functions-list-3-0.8.0.sol-0.8.15-compact.json b/tests/ast-parsing/expected/using-for-functions-list-3-0.8.0.sol-0.8.15-compact.json new file mode 100644 index 000000000..a41d2ba18 --- /dev/null +++ b/tests/ast-parsing/expected/using-for-functions-list-3-0.8.0.sol-0.8.15-compact.json @@ -0,0 +1,10 @@ +{ + "L1": { + "a(Data,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n", + "b(Data,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n", + "c(Data,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n" + }, + "C": { + "topLevelCall(uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/using-for-functions-list-4-0.8.0.sol-0.8.15-compact.json b/tests/ast-parsing/expected/using-for-functions-list-4-0.8.0.sol-0.8.15-compact.json new file mode 100644 index 000000000..01536efb9 --- /dev/null +++ b/tests/ast-parsing/expected/using-for-functions-list-4-0.8.0.sol-0.8.15-compact.json @@ -0,0 +1,10 @@ +{ + "L1": { + "a(Data,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n", + "b(Data,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n", + "c(Data,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n" + }, + "C": { + "libCall(uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/using-for-global-0.8.0.sol-0.8.15-compact.json b/tests/ast-parsing/expected/using-for-global-0.8.0.sol-0.8.15-compact.json new file mode 100644 index 000000000..f2119ecf1 --- /dev/null +++ b/tests/ast-parsing/expected/using-for-global-0.8.0.sol-0.8.15-compact.json @@ -0,0 +1,11 @@ +{ + "C": { + "libCall(uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n", + "topLevelCall(uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + }, + "L1": { + "a(Data,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n", + "b(Data,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n", + "c(Data,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/ast-parsing/using-for-1-0.8.0.sol b/tests/ast-parsing/using-for-1-0.8.0.sol new file mode 100644 index 000000000..201e18527 --- /dev/null +++ b/tests/ast-parsing/using-for-1-0.8.0.sol @@ -0,0 +1,40 @@ + +struct Data { mapping(uint => bool) flags; } +using L1 for Data; + +function d(Data storage self, uint value) returns(bool){ + return true; +} + +library L1 { + function a(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + + function b(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + + function c(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + +} + +contract C { + Data knownValues; + + function libCall(uint value) public { + require(knownValues.a(value)); + } + +} diff --git a/tests/ast-parsing/using-for-2-0.8.0.sol b/tests/ast-parsing/using-for-2-0.8.0.sol new file mode 100644 index 000000000..cb0bb8ba1 --- /dev/null +++ b/tests/ast-parsing/using-for-2-0.8.0.sol @@ -0,0 +1,40 @@ + +struct Data { mapping(uint => bool) flags; } + +function d(Data storage self, uint value) returns(bool){ + return true; +} + +library L1 { + function a(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + + function b(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + + function c(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + +} + +contract C { + using L1 for Data; + Data knownValues; + + function libCall(uint value) public { + require(knownValues.a(value)); + } + +} diff --git a/tests/ast-parsing/using-for-functions-list-1-0.8.0.sol b/tests/ast-parsing/using-for-functions-list-1-0.8.0.sol new file mode 100644 index 000000000..52ed5087e --- /dev/null +++ b/tests/ast-parsing/using-for-functions-list-1-0.8.0.sol @@ -0,0 +1,37 @@ + +struct Data { mapping(uint => bool) flags; } + +library L1 { + function a(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + + function b(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + + function c(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + +} + +contract C { + + using {L1.a, L1.b} for Data; + Data knownValues; + + function libCall(uint value) public { + require(knownValues.a(value)); + } + +} diff --git a/tests/ast-parsing/using-for-functions-list-2-0.8.0.sol b/tests/ast-parsing/using-for-functions-list-2-0.8.0.sol new file mode 100644 index 000000000..d41163dee --- /dev/null +++ b/tests/ast-parsing/using-for-functions-list-2-0.8.0.sol @@ -0,0 +1,41 @@ + +struct Data { mapping(uint => bool) flags; } + +function d(Data storage self, uint value) returns(bool){ + return true; +} + +library L1 { + function a(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + + function b(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + + function c(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + +} + +contract C { + + using {L1.a, L1.b, d} for Data; + Data knownValues; + + function topLevelCall(uint value) public { + require(knownValues.d(value)); + } + +} diff --git a/tests/ast-parsing/using-for-functions-list-3-0.8.0.sol b/tests/ast-parsing/using-for-functions-list-3-0.8.0.sol new file mode 100644 index 000000000..7e5db5776 --- /dev/null +++ b/tests/ast-parsing/using-for-functions-list-3-0.8.0.sol @@ -0,0 +1,41 @@ + +struct Data { mapping(uint => bool) flags; } +using {L1.a, L1.b, d} for Data; + +function d(Data storage self, uint value) returns(bool){ + return true; +} + +library L1 { + function a(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + + function b(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + + function c(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + +} + +contract C { + + Data knownValues; + + function topLevelCall(uint value) public { + require(knownValues.d(value)); + } + +} diff --git a/tests/ast-parsing/using-for-functions-list-4-0.8.0.sol b/tests/ast-parsing/using-for-functions-list-4-0.8.0.sol new file mode 100644 index 000000000..ecce5e764 --- /dev/null +++ b/tests/ast-parsing/using-for-functions-list-4-0.8.0.sol @@ -0,0 +1,40 @@ + +struct Data { mapping(uint => bool) flags; } +using {L1.a, L1.b, d} for Data; + +function d(Data storage self, uint value) returns(bool){ + return true; +} + +library L1 { + function a(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + + function b(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + + function c(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + +} + +contract C { + Data knownValues; + + function libCall(uint value) public { + require(knownValues.a(value)); + } + +} diff --git a/tests/ast-parsing/using-for-global-0.8.0.sol b/tests/ast-parsing/using-for-global-0.8.0.sol new file mode 100644 index 000000000..c5b112259 --- /dev/null +++ b/tests/ast-parsing/using-for-global-0.8.0.sol @@ -0,0 +1,15 @@ +import "./using-for-library-0.8.0.sol"; + +contract C { + Data knownValues; + + function libCall(uint value) public { + require(knownValues.a(value)); + } + + function topLevelCall(uint value) public { + require(knownValues.d(value)); + } + + +} diff --git a/tests/ast-parsing/using-for-library-0.8.0.sol b/tests/ast-parsing/using-for-library-0.8.0.sol new file mode 100644 index 000000000..97ee39ca6 --- /dev/null +++ b/tests/ast-parsing/using-for-library-0.8.0.sol @@ -0,0 +1,32 @@ + +struct Data { mapping(uint => bool) flags; } +using L1 for Data global; +using {d} for Data global; + +function d(Data storage self, uint value) returns(bool){ + return true; +} + +library L1 { + function a(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + + function b(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + + function c(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + +} diff --git a/tests/test_ast_parsing.py b/tests/test_ast_parsing.py index 506ee3d6b..77d0b86cd 100644 --- a/tests/test_ast_parsing.py +++ b/tests/test_ast_parsing.py @@ -420,6 +420,13 @@ ALL_TESTS = [ Test("free_functions/new_operator.sol", ["0.8.12"]), Test("free_functions/library_constant_function_collision.sol", ["0.8.12"]), Test("ternary-with-max.sol", ["0.8.15"]), + Test("using-for-1-0.8.0.sol", ["0.8.15"]), + Test("using-for-2-0.8.0.sol", ["0.8.15"]), + Test("using-for-functions-list-1-0.8.0.sol", ["0.8.15"]), + Test("using-for-functions-list-2-0.8.0.sol", ["0.8.15"]), + Test("using-for-functions-list-3-0.8.0.sol", ["0.8.15"]), + Test("using-for-functions-list-4-0.8.0.sol", ["0.8.15"]), + Test("using-for-global-0.8.0.sol", ["0.8.15"]), ] # create the output folder if needed try: From cc3b342d517b7cce9804bd9e1380c5b4326b52c5 Mon Sep 17 00:00:00 2001 From: Simone Date: Sat, 27 Aug 2022 11:14:56 +0200 Subject: [PATCH 03/25] Fix ir for top level function --- slither/slithir/convert.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/slither/slithir/convert.py b/slither/slithir/convert.py index 7658deb26..e851acfcc 100644 --- a/slither/slithir/convert.py +++ b/slither/slithir/convert.py @@ -15,6 +15,7 @@ from slither.core.declarations import ( ) from slither.core.declarations.custom_error import CustomError from slither.core.declarations.function_contract import FunctionContract +from slither.core.declarations.function_top_level import FunctionTopLevel from slither.core.declarations.solidity_import_placeholder import SolidityImportPlaceHolder from slither.core.declarations.solidity_variables import SolidityCustomRevert from slither.core.expressions import Identifier, Literal @@ -527,9 +528,9 @@ def propagate_types(ir, node: "Node"): # pylint: disable=too-many-locals if can_be_solidity_func(ir): return convert_to_solidity_func(ir) - # convert library + # convert library or top level function if t in using_for or "*" in using_for: - new_ir = convert_to_library(ir, node, using_for) + new_ir = convert_to_library_or_top_level(ir, node, using_for) if new_ir: return new_ir @@ -1331,8 +1332,24 @@ def convert_to_pop(ir, node): return ret -def look_for_library(contract, ir, using_for, t): +def look_for_library_or_top_level(contract, ir, using_for, t): for destination in using_for[t]: + if isinstance(destination, FunctionTopLevel) and destination.name == ir.function_name: + internalcall = InternalCall(destination, ir.nbr_arguments, ir.lvalue, ir.type_call) + internalcall.set_expression(ir.expression) + internalcall.set_node(ir.node) + internalcall.call_gas = ir.call_gas + internalcall.arguments = [ir.destination] + ir.arguments + return_type = internalcall.function.return_type + if return_type: + if len(return_type) == 1: + internalcall.lvalue.set_type(return_type[0]) + elif len(return_type) > 1: + internalcall.lvalue.set_type(return_type) + else: + internalcall.lvalue = None + return internalcall + if isinstance(destination, FunctionContract) and destination.contract.is_library: lib_contract = destination.contract else: @@ -1356,19 +1373,19 @@ def look_for_library(contract, ir, using_for, t): return None -def convert_to_library(ir, node, using_for): +def convert_to_library_or_top_level(ir, node, using_for): # We use contract_declarer, because Solidity resolve the library # before resolving the inheritance. # Though we could use .contract as libraries cannot be shadowed contract = node.function.contract_declarer t = ir.destination.type if t in using_for: - new_ir = look_for_library(contract, ir, using_for, t) + new_ir = look_for_library_or_top_level(contract, ir, using_for, t) if new_ir: return new_ir if "*" in using_for: - new_ir = look_for_library(contract, ir, using_for, "*") + new_ir = look_for_library_or_top_level(contract, ir, using_for, "*") if new_ir: return new_ir From 928d7f22ceee06f376e6d227e2bf28a525f3fed7 Mon Sep 17 00:00:00 2001 From: Simone Date: Mon, 19 Sep 2022 22:54:33 +0200 Subject: [PATCH 04/25] Run black --- .../core/solidity_types/elementary_type.py | 6 ++-- .../naming_convention/naming_convention.py | 12 +++++--- .../statements/type_based_tautology.py | 2 +- .../declarations/using_for_top_level.py | 29 +++++++++---------- slither/utils/integer_conversion.py | 2 +- .../visitors/expression/constants_folding.py | 2 +- 6 files changed, 27 insertions(+), 26 deletions(-) diff --git a/slither/core/solidity_types/elementary_type.py b/slither/core/solidity_types/elementary_type.py index fc248e946..c6804f9c1 100644 --- a/slither/core/solidity_types/elementary_type.py +++ b/slither/core/solidity_types/elementary_type.py @@ -43,8 +43,8 @@ Int = [ "int256", ] -Max_Int = {k: 2 ** (8 * i - 1) - 1 if i > 0 else 2**255 - 1 for i, k in enumerate(Int)} -Min_Int = {k: -(2 ** (8 * i - 1)) if i > 0 else -(2**255) for i, k in enumerate(Int)} +Max_Int = {k: 2 ** (8 * i - 1) - 1 if i > 0 else 2 ** 255 - 1 for i, k in enumerate(Int)} +Min_Int = {k: -(2 ** (8 * i - 1)) if i > 0 else -(2 ** 255) for i, k in enumerate(Int)} Uint = [ "uint", @@ -82,7 +82,7 @@ Uint = [ "uint256", ] -Max_Uint = {k: 2 ** (8 * i) - 1 if i > 0 else 2**256 - 1 for i, k in enumerate(Uint)} +Max_Uint = {k: 2 ** (8 * i) - 1 if i > 0 else 2 ** 256 - 1 for i, k in enumerate(Uint)} Min_Uint = {k: 0 for k in Uint} diff --git a/slither/detectors/naming_convention/naming_convention.py b/slither/detectors/naming_convention/naming_convention.py index 706f4ae6c..2cca9dee9 100644 --- a/slither/detectors/naming_convention/naming_convention.py +++ b/slither/detectors/naming_convention/naming_convention.py @@ -89,10 +89,14 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2 if func.is_constructor: continue if not self.is_mixed_case(func.name): - if func.visibility in [ - "internal", - "private", - ] and self.is_mixed_case_with_underscore(func.name): + if ( + func.visibility + in [ + "internal", + "private", + ] + and self.is_mixed_case_with_underscore(func.name) + ): continue if func.name.startswith(("echidna_", "crytic_")): continue diff --git a/slither/detectors/statements/type_based_tautology.py b/slither/detectors/statements/type_based_tautology.py index 0129ad03f..efa814713 100644 --- a/slither/detectors/statements/type_based_tautology.py +++ b/slither/detectors/statements/type_based_tautology.py @@ -11,7 +11,7 @@ from slither.core.solidity_types.elementary_type import Int, Uint def typeRange(t): bits = int(t.split("int")[1]) if t in Uint: - return 0, (2**bits) - 1 + return 0, (2 ** bits) - 1 if t in Int: v = (2 ** (bits - 1)) - 1 return -v, v diff --git a/slither/solc_parsing/declarations/using_for_top_level.py b/slither/solc_parsing/declarations/using_for_top_level.py index 070ed8cf7..c3a6cb55e 100644 --- a/slither/solc_parsing/declarations/using_for_top_level.py +++ b/slither/solc_parsing/declarations/using_for_top_level.py @@ -6,10 +6,8 @@ from typing import TYPE_CHECKING, Dict, Union from slither.core.compilation_unit import SlitherCompilationUnit from slither.core.declarations.using_for_top_level import UsingForTopLevel -from slither.core.solidity_types import Type, TypeAliasTopLevel +from slither.core.solidity_types import TypeAliasTopLevel from slither.core.declarations import ( - FunctionContract, - FunctionTopLevel, StructureTopLevel, EnumTopLevel, ) @@ -28,15 +26,12 @@ class UsingForTopLevelSolc(CallerContextExpression): # pylint: disable=too-few- UsingFor class """ - # elems = [(type, name)] - def __init__( # pylint: disable=too-many-arguments self, uftl: UsingForTopLevel, top_level_data: Dict, slither_parser: "SlitherCompilationUnitSolc", ): - # TODO think if save global here is useful self._type_name = top_level_data["typeName"] self._global = top_level_data["global"] @@ -48,14 +43,14 @@ class UsingForTopLevelSolc(CallerContextExpression): # pylint: disable=too-few- self._using_for = uftl self._slither_parser = slither_parser - def analyze(self): + def analyze(self) -> None: type_name = parse_type(self._type_name, self) self._using_for.using_for[type_name] = [] if hasattr(self, "_library_name"): library_name = parse_type(self._library_name, self) self._using_for.using_for[type_name].append(library_name) - self._propagate_global(type_name, library_name) + self._propagate_global(type_name) else: for f in self._functions: full_name_split = f["function"]["name"].split(".") @@ -65,7 +60,8 @@ class UsingForTopLevelSolc(CallerContextExpression): # pylint: disable=too-few- for tl_function in self.compilation_unit.functions_top_level: if tl_function.name == function_name: self._using_for.using_for[type_name].append(tl_function) - self._propagate_global(type_name, tl_function) + self._propagate_global(type_name) + break elif len(full_name_split) == 2: # Library function library_name = full_name_split[0] @@ -78,17 +74,18 @@ class UsingForTopLevelSolc(CallerContextExpression): # pylint: disable=too-few- for cf in c.functions: if cf.name == function_name: self._using_for.using_for[type_name].append(cf) - self._propagate_global(type_name, cf) + self._propagate_global(type_name) found = True break else: # probably case if there is an import with an alias we don't handle it for now # e.g. MyImport.MyLib.a - return + LOGGER.warning( + f"Using for directive for function {f['function']['name']} not supported" + ) + continue - def _propagate_global( - self, type_name: Type, to_add: Union[FunctionTopLevel, FunctionContract, UserDefinedType] - ): + def _propagate_global(self, type_name: Union[TypeAliasTopLevel, UserDefinedType]) -> None: if self._global: for scope in self.compilation_unit.scopes.values(): if isinstance(type_name, TypeAliasTopLevel): @@ -107,11 +104,11 @@ class UsingForTopLevelSolc(CallerContextExpression): # pylint: disable=too-few- scope.usingFor.add(self._using_for) else: LOGGER.error( - f"Error propagating global {underlying} {type(underlying)} not a StructTopLevel or EnumTopLevel" + f"Error when propagating global {underlying} {type(underlying)} not a StructTopLevel or EnumTopLevel" ) else: LOGGER.error( - f"Found {to_add} {type(to_add)} when propagating global using for {type_name} {type(type_name)}" + f"Error when propagating global using for {type_name} {type(type_name)}" ) @property diff --git a/slither/utils/integer_conversion.py b/slither/utils/integer_conversion.py index 8481e8641..f5cf453b5 100644 --- a/slither/utils/integer_conversion.py +++ b/slither/utils/integer_conversion.py @@ -23,6 +23,6 @@ def convert_string_to_int(val: Union[str, int]) -> int: f"{base}e{expo} is too large to fit in any Solidity integer size" ) return 0 - return int(Decimal(base) * Decimal(10**expo)) + return int(Decimal(base) * Decimal(10 ** expo)) return int(Decimal(val)) diff --git a/slither/visitors/expression/constants_folding.py b/slither/visitors/expression/constants_folding.py index 61c98d65f..1758c83dd 100644 --- a/slither/visitors/expression/constants_folding.py +++ b/slither/visitors/expression/constants_folding.py @@ -43,7 +43,7 @@ class ConstantFolding(ExpressionVisitor): left = get_val(expression.expression_left) right = get_val(expression.expression_right) if expression.type == BinaryOperationType.POWER: - set_val(expression, left**right) + set_val(expression, left ** right) elif expression.type == BinaryOperationType.MULTIPLICATION: set_val(expression, left * right) elif expression.type == BinaryOperationType.DIVISION: From 6571ada9a2f26b4cb1ef3e48196864ff577480b4 Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Mon, 3 Oct 2022 10:57:48 +0200 Subject: [PATCH 05/25] run black --- slither/core/solidity_types/elementary_type.py | 6 +++--- .../detectors/naming_convention/naming_convention.py | 12 ++++-------- slither/detectors/statements/type_based_tautology.py | 2 +- slither/slithir/convert.py | 4 ++-- slither/visitors/expression/constants_folding.py | 2 +- 5 files changed, 11 insertions(+), 15 deletions(-) diff --git a/slither/core/solidity_types/elementary_type.py b/slither/core/solidity_types/elementary_type.py index c6804f9c1..fc248e946 100644 --- a/slither/core/solidity_types/elementary_type.py +++ b/slither/core/solidity_types/elementary_type.py @@ -43,8 +43,8 @@ Int = [ "int256", ] -Max_Int = {k: 2 ** (8 * i - 1) - 1 if i > 0 else 2 ** 255 - 1 for i, k in enumerate(Int)} -Min_Int = {k: -(2 ** (8 * i - 1)) if i > 0 else -(2 ** 255) for i, k in enumerate(Int)} +Max_Int = {k: 2 ** (8 * i - 1) - 1 if i > 0 else 2**255 - 1 for i, k in enumerate(Int)} +Min_Int = {k: -(2 ** (8 * i - 1)) if i > 0 else -(2**255) for i, k in enumerate(Int)} Uint = [ "uint", @@ -82,7 +82,7 @@ Uint = [ "uint256", ] -Max_Uint = {k: 2 ** (8 * i) - 1 if i > 0 else 2 ** 256 - 1 for i, k in enumerate(Uint)} +Max_Uint = {k: 2 ** (8 * i) - 1 if i > 0 else 2**256 - 1 for i, k in enumerate(Uint)} Min_Uint = {k: 0 for k in Uint} diff --git a/slither/detectors/naming_convention/naming_convention.py b/slither/detectors/naming_convention/naming_convention.py index 2cca9dee9..706f4ae6c 100644 --- a/slither/detectors/naming_convention/naming_convention.py +++ b/slither/detectors/naming_convention/naming_convention.py @@ -89,14 +89,10 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2 if func.is_constructor: continue if not self.is_mixed_case(func.name): - if ( - func.visibility - in [ - "internal", - "private", - ] - and self.is_mixed_case_with_underscore(func.name) - ): + if func.visibility in [ + "internal", + "private", + ] and self.is_mixed_case_with_underscore(func.name): continue if func.name.startswith(("echidna_", "crytic_")): continue diff --git a/slither/detectors/statements/type_based_tautology.py b/slither/detectors/statements/type_based_tautology.py index efa814713..0129ad03f 100644 --- a/slither/detectors/statements/type_based_tautology.py +++ b/slither/detectors/statements/type_based_tautology.py @@ -11,7 +11,7 @@ from slither.core.solidity_types.elementary_type import Int, Uint def typeRange(t): bits = int(t.split("int")[1]) if t in Uint: - return 0, (2 ** bits) - 1 + return 0, (2**bits) - 1 if t in Int: v = (2 ** (bits - 1)) - 1 return -v, v diff --git a/slither/slithir/convert.py b/slither/slithir/convert.py index e851acfcc..818959afb 100644 --- a/slither/slithir/convert.py +++ b/slither/slithir/convert.py @@ -172,10 +172,10 @@ def _fits_under_integer(val: int, can_be_int: bool, can_be_uint) -> List[str]: assert can_be_int | can_be_uint while n <= 256: if can_be_uint: - if val <= 2 ** n - 1: + if val <= 2**n - 1: ret.append(f"uint{n}") if can_be_int: - if val <= (2 ** n) / 2 - 1: + if val <= (2**n) / 2 - 1: ret.append(f"int{n}") n = n + 8 return ret diff --git a/slither/visitors/expression/constants_folding.py b/slither/visitors/expression/constants_folding.py index 250104f5e..b324ed842 100644 --- a/slither/visitors/expression/constants_folding.py +++ b/slither/visitors/expression/constants_folding.py @@ -43,7 +43,7 @@ class ConstantFolding(ExpressionVisitor): left = get_val(expression.expression_left) right = get_val(expression.expression_right) if expression.type == BinaryOperationType.POWER: - set_val(expression, left ** right) + set_val(expression, left**right) elif expression.type == BinaryOperationType.MULTIPLICATION: set_val(expression, left * right) elif expression.type == BinaryOperationType.DIVISION: From 3278417af0a8878613818267b7f936097d305ce4 Mon Sep 17 00:00:00 2001 From: Simone Date: Mon, 3 Oct 2022 13:56:35 +0200 Subject: [PATCH 06/25] Refactor parsing --- slither/solc_parsing/declarations/contract.py | 81 +++++++++---------- .../declarations/using_for_top_level.py | 81 ++++++++++++------- .../slither_compilation_unit_solc.py | 13 ++- 3 files changed, 95 insertions(+), 80 deletions(-) diff --git a/slither/solc_parsing/declarations/contract.py b/slither/solc_parsing/declarations/contract.py index 32ca0e8ab..c66802844 100644 --- a/slither/solc_parsing/declarations/contract.py +++ b/slither/solc_parsing/declarations/contract.py @@ -5,7 +5,7 @@ from slither.core.declarations import Modifier, Event, EnumContract, StructureCo from slither.core.declarations.contract import Contract from slither.core.declarations.custom_error_contract import CustomErrorContract from slither.core.declarations.function_contract import FunctionContract -from slither.core.solidity_types import ElementaryType, TypeAliasContract +from slither.core.solidity_types import ElementaryType, TypeAliasContract, Type from slither.core.variables.state_variable import StateVariable from slither.solc_parsing.declarations.caller_context import CallerContextExpression from slither.solc_parsing.declarations.custom_error import CustomErrorSolc @@ -574,7 +574,7 @@ class ContractSolc(CallerContextExpression): except (VariableNotFound, KeyError) as e: self.log_incorrect_parsing(f"Missing state variable {e}") - def analyze_using_for(self): + def analyze_using_for(self): # pylint: disable=too-many-branches try: for father in self._contract.inheritance: self._contract.using_for.update(father.using_for) @@ -593,18 +593,7 @@ class ContractSolc(CallerContextExpression): ) else: # We have a list of functions. A function can be topLevel or a library function - # at this point library function are yet to be parsed so we add the function name - # and add the real function later - for f in using_for["functionList"]: - function_name = f["function"]["name"] - if function_name.find(".") != -1: - # Library function - self._contract.using_for[type_name].append(function_name) - else: - # Top level function - for tl_function in self.compilation_unit.functions_top_level: - if tl_function.name == function_name: - self._contract.using_for[type_name].append(tl_function) + self._analyze_function_list(using_for["functionList"], type_name) else: for using_for in self._usingForNotParsed: children = using_for[self.get_children()] @@ -622,34 +611,42 @@ class ContractSolc(CallerContextExpression): except (VariableNotFound, KeyError) as e: self.log_incorrect_parsing(f"Missing using for {e}") - def analyze_library_function_using_for(self): - for type_name, full_names in self._contract.using_for.items(): - # If it's a string is a library function e.g. L.a - # We add the actual function and remove the string - for full_name in full_names: - if isinstance(full_name, str): - full_name_split = full_name.split(".") - # TODO this doesn't handle the case if there is an import with an alias - # e.g. MyImport.MyLib.a - if len(full_name_split) == 2: - library_name = full_name_split[0] - function_name = full_name_split[1] - # Get the library function - found = False - for c in self.compilation_unit.contracts: - if found: - break - if c.name == library_name: - for f in c.functions: - if f.name == function_name: - self._contract.using_for[type_name].append(f) - found = True - break - self._contract.using_for[type_name].remove(full_name) - else: - self.log_incorrect_parsing( - f"Expected library function instead received {full_name}" - ) + def _analyze_function_list(self, function_list: List, type_name: Type): + for f in function_list: + function_name = f["function"]["name"] + if function_name.find(".") != -1: + # Library function + self._analyze_library_function(function_name, type_name) + else: + # Top level function + for tl_function in self.compilation_unit.functions_top_level: + if tl_function.name == function_name: + self._contract.using_for[type_name].append(tl_function) + + def _analyze_library_function(self, function_name: str, type_name: Type) -> None: + function_name_split = function_name.split(".") + # TODO this doesn't handle the case if there is an import with an alias + # e.g. MyImport.MyLib.a + if len(function_name_split) == 2: + library_name = function_name_split[0] + function_name = function_name_split[1] + # Get the library function + found = False + for c in self.compilation_unit.contracts: + if found: + break + if c.name == library_name: + for f in c.functions: + if f.name == function_name: + self._contract.using_for[type_name].append(f) + found = True + break + if not found: + self.log_incorrect_parsing(f"Library function not found {function_name}") + else: + self.log_incorrect_parsing( + f"Expected library function instead received {function_name}" + ) def analyze_enums(self): try: diff --git a/slither/solc_parsing/declarations/using_for_top_level.py b/slither/solc_parsing/declarations/using_for_top_level.py index c3a6cb55e..89d1dc276 100644 --- a/slither/solc_parsing/declarations/using_for_top_level.py +++ b/slither/solc_parsing/declarations/using_for_top_level.py @@ -2,10 +2,11 @@ Using For Top Level module """ import logging -from typing import TYPE_CHECKING, Dict, Union +from typing import TYPE_CHECKING, Dict, Union, Any from slither.core.compilation_unit import SlitherCompilationUnit from slither.core.declarations.using_for_top_level import UsingForTopLevel +from slither.core.scope.scope import FileScope from slither.core.solidity_types import TypeAliasTopLevel from slither.core.declarations import ( StructureTopLevel, @@ -57,26 +58,12 @@ class UsingForTopLevelSolc(CallerContextExpression): # pylint: disable=too-few- if len(full_name_split) == 1: # Top level function function_name = full_name_split[0] - for tl_function in self.compilation_unit.functions_top_level: - if tl_function.name == function_name: - self._using_for.using_for[type_name].append(tl_function) - self._propagate_global(type_name) - break + self._analyze_top_level_function(function_name, type_name) elif len(full_name_split) == 2: # Library function library_name = full_name_split[0] function_name = full_name_split[1] - found = False - for c in self.compilation_unit.contracts: - if found: - break - if c.name == library_name: - for cf in c.functions: - if cf.name == function_name: - self._using_for.using_for[type_name].append(cf) - self._propagate_global(type_name) - found = True - break + self._analyze_library_function(function_name, library_name, type_name) else: # probably case if there is an import with an alias we don't handle it for now # e.g. MyImport.MyLib.a @@ -85,6 +72,35 @@ class UsingForTopLevelSolc(CallerContextExpression): # pylint: disable=too-few- ) continue + def _analyze_top_level_function( + self, function_name: str, type_name: Union[TypeAliasTopLevel, UserDefinedType] + ) -> None: + for tl_function in self.compilation_unit.functions_top_level: + if tl_function.name == function_name: + self._using_for.using_for[type_name].append(tl_function) + self._propagate_global(type_name) + break + + def _analyze_library_function( + self, + function_name: str, + library_name: str, + type_name: Union[TypeAliasTopLevel, UserDefinedType], + ) -> None: + found = False + for c in self.compilation_unit.contracts: + if found: + break + if c.name == library_name: + for cf in c.functions: + if cf.name == function_name: + self._using_for.using_for[type_name].append(cf) + self._propagate_global(type_name) + found = True + break + if not found: + LOGGER.warning(f"Library {library_name} - function {function_name} not found") + def _propagate_global(self, type_name: Union[TypeAliasTopLevel, UserDefinedType]) -> None: if self._global: for scope in self.compilation_unit.scopes.values(): @@ -93,24 +109,29 @@ class UsingForTopLevelSolc(CallerContextExpression): # pylint: disable=too-few- if alias == type_name: scope.usingFor.add(self._using_for) elif isinstance(type_name, UserDefinedType): - underlying = type_name.type - if isinstance(underlying, StructureTopLevel): - for struct in scope.structures.values(): - if struct == underlying: - scope.usingFor.add(self._using_for) - elif isinstance(underlying, EnumTopLevel): - for enum in scope.enums.values(): - if enum == underlying: - scope.usingFor.add(self._using_for) - else: - LOGGER.error( - f"Error when propagating global {underlying} {type(underlying)} not a StructTopLevel or EnumTopLevel" - ) + self._propagate_global_UserDefinedType(scope, type_name) else: LOGGER.error( f"Error when propagating global using for {type_name} {type(type_name)}" ) + def _propagate_global_UserDefinedType( + self, scope: Dict[Any, FileScope], type_name: UserDefinedType + ): + underlying = type_name.type + if isinstance(underlying, StructureTopLevel): + for struct in scope.structures.values(): + if struct == underlying: + scope.usingFor.add(self._using_for) + elif isinstance(underlying, EnumTopLevel): + for enum in scope.enums.values(): + if enum == underlying: + scope.usingFor.add(self._using_for) + else: + LOGGER.error( + f"Error when propagating global {underlying} {type(underlying)} not a StructTopLevel or EnumTopLevel" + ) + @property def is_compact_ast(self) -> bool: return self._slither_parser.is_compact_ast diff --git a/slither/solc_parsing/slither_compilation_unit_solc.py b/slither/solc_parsing/slither_compilation_unit_solc.py index 828229c0c..f08a71487 100644 --- a/slither/solc_parsing/slither_compilation_unit_solc.py +++ b/slither/solc_parsing/slither_compilation_unit_solc.py @@ -509,11 +509,7 @@ Please rename it, this name is reserved for Slither's internals""" # Then we analyse state variables, functions and modifiers self._analyze_third_part(contracts_to_be_analyzed, libraries) - self._analyze_top_level_using_for() - - # Convert library function (at the moment are string) in using for that specifies list of functions - # to actual function - self._analyze_library_function_using_for(contracts_to_be_analyzed) + self._analyze_using_for(contracts_to_be_analyzed) self._parsed = True @@ -625,9 +621,11 @@ Please rename it, this name is reserved for Slither's internals""" else: contracts_to_be_analyzed += [contract] - def _analyze_library_function_using_for(self, contracts_to_be_analyzed: List[ContractSolc]): + def _analyze_using_for(self, contracts_to_be_analyzed: List[ContractSolc]): + self._analyze_top_level_using_for() + for c in contracts_to_be_analyzed: - c.analyze_library_function_using_for() + c.analyze_using_for() def _analyze_enums(self, contract: ContractSolc): # Enum must be analyzed first @@ -651,7 +649,6 @@ Please rename it, this name is reserved for Slither's internals""" # Event can refer to struct contract.analyze_events() - contract.analyze_using_for() contract.analyze_custom_errors() contract.set_is_analyzed(True) From cfb53e8200e6d598ab4e7b8447697f3ef29a1fa5 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Sat, 3 Dec 2022 12:06:37 -0600 Subject: [PATCH 07/25] support ternary in call value --- slither/utils/expression_manipulations.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/slither/utils/expression_manipulations.py b/slither/utils/expression_manipulations.py index 1a300c39b..3e12ae8c1 100644 --- a/slither/utils/expression_manipulations.py +++ b/slither/utils/expression_manipulations.py @@ -28,15 +28,18 @@ def f_expressions( e._expressions.append(x) -def f_call(e, x): +def f_call(e: CallExpression, x): e._arguments.append(x) +def f_call_value(e: CallExpression, x): + e._value = x + def f_expression(e, x): e._expression = x -def f_called(e, x): +def f_called(e: CallExpression, x): e._called = x @@ -123,6 +126,15 @@ class SplitTernaryExpression: if self.apply_copy(next_expr, true_expression, false_expression, f_called): self.copy_expression(next_expr, true_expression.called, false_expression.called) + next_expr = expression.call_value + # case of (..).func{value: .. ? .. : ..}() + if self.apply_copy(next_expr, true_expression, false_expression, f_call_value): + self.copy_expression( + next_expr, + true_expression.call_value, + false_expression.call_value, + ) + true_expression._arguments = [] false_expression._arguments = [] From eb49e396fd0d60c10e4db1771b443319e1faf55b Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Sat, 10 Dec 2022 15:41:39 -0600 Subject: [PATCH 08/25] support ternaries in both call options, refactor index access --- slither/solc_parsing/declarations/function.py | 4 +- slither/utils/expression_manipulations.py | 116 ++++++++++-------- tests/slithir/ternary_expressions.sol | 11 ++ 3 files changed, 75 insertions(+), 56 deletions(-) diff --git a/slither/solc_parsing/declarations/function.py b/slither/solc_parsing/declarations/function.py index 130375211..269ca580f 100644 --- a/slither/solc_parsing/declarations/function.py +++ b/slither/solc_parsing/declarations/function.py @@ -308,7 +308,7 @@ class FunctionSolc(CallerContextExpression): for node_parser in self._node_to_yulobject.values(): node_parser.analyze_expressions() - self._filter_ternary() + self._rewrite_ternary_as_if_else() self._remove_alone_endif() @@ -1336,7 +1336,7 @@ class FunctionSolc(CallerContextExpression): ################################################################################### ################################################################################### - def _filter_ternary(self) -> bool: + def _rewrite_ternary_as_if_else(self) -> bool: ternary_found = True updated = False while ternary_found: diff --git a/slither/utils/expression_manipulations.py b/slither/utils/expression_manipulations.py index 3e12ae8c1..777c0c2a1 100644 --- a/slither/utils/expression_manipulations.py +++ b/slither/utils/expression_manipulations.py @@ -3,7 +3,7 @@ as they should be immutable """ import copy -from typing import Union, Callable +from typing import Union, Callable, Tuple, Optional from slither.core.expressions import UnaryOperation from slither.core.expressions.assignment_operation import AssignmentOperation from slither.core.expressions.binary_operation import BinaryOperation @@ -35,6 +35,11 @@ def f_call(e: CallExpression, x): def f_call_value(e: CallExpression, x): e._value = x + +def f_call_gas(e: CallExpression, x): + e._gas = x + + def f_expression(e, x): e._expression = x @@ -56,7 +61,7 @@ class SplitTernaryExpression: self.condition = None self.copy_expression(expression, self.true_expression, self.false_expression) - def apply_copy( + def conditional_not_ahead( self, next_expr: Expression, true_expression: Union[AssignmentOperation, MemberAccess], @@ -94,7 +99,9 @@ class SplitTernaryExpression: # (.. ? .. : ..).add if isinstance(expression, MemberAccess): next_expr = expression.expression - if self.apply_copy(next_expr, true_expression, false_expression, f_expression): + if self.conditional_not_ahead( + next_expr, true_expression, false_expression, f_expression + ): self.copy_expression( next_expr, true_expression.expression, false_expression.expression ) @@ -102,44 +109,75 @@ class SplitTernaryExpression: elif isinstance(expression, (AssignmentOperation, BinaryOperation, TupleExpression)): true_expression._expressions = [] false_expression._expressions = [] - for next_expr in expression.expressions: - if isinstance(next_expr, IndexAccess): - # create an index access for each branch - if isinstance(next_expr.expression_right, ConditionalExpression): - next_expr = _handle_ternary_access( - next_expr, true_expression, false_expression + # TODO: can we get rid of `NoneType` expressions in `TupleExpression`? + if next_expr: + if isinstance(next_expr, IndexAccess): + # create an index access for each branch + # x[if cond ? 1 : 2] -> if cond { x[1] } else { x[2] } + for expr in next_expr.expressions: + if self.conditional_not_ahead( + expr, true_expression, false_expression, f_expressions + ): + self.copy_expression( + expr, + true_expression.expressions[-1], + false_expression.expressions[-1], + ) + + if self.conditional_not_ahead( + next_expr, true_expression, false_expression, f_expressions + ): + # always on last arguments added + self.copy_expression( + next_expr, + true_expression.expressions[-1], + false_expression.expressions[-1], ) - if self.apply_copy(next_expr, true_expression, false_expression, f_expressions): - # always on last arguments added - self.copy_expression( - next_expr, - true_expression.expressions[-1], - false_expression.expressions[-1], - ) elif isinstance(expression, CallExpression): next_expr = expression.called # case of lib # (.. ? .. : ..).add - if self.apply_copy(next_expr, true_expression, false_expression, f_called): + if self.conditional_not_ahead(next_expr, true_expression, false_expression, f_called): self.copy_expression(next_expr, true_expression.called, false_expression.called) - next_expr = expression.call_value - # case of (..).func{value: .. ? .. : ..}() - if self.apply_copy(next_expr, true_expression, false_expression, f_call_value): + # In order to handle ternaries in both call options, gas and value, we return early if the + # conditional is not ahead to rewrite both ternaries (see `_rewrite_ternary_as_if_else`). + if expression.call_gas: + # case of (..).func{gas: .. ? .. : ..}() + next_expr = expression.call_gas + if self.conditional_not_ahead( + next_expr, true_expression, false_expression, f_call_gas + ): + self.copy_expression( + next_expr, + true_expression.call_gas, + false_expression.call_gas, + ) + else: + return + + if expression.call_value: + # case of (..).func{value: .. ? .. : ..}() + next_expr = expression.call_value + if self.conditional_not_ahead( + next_expr, true_expression, false_expression, f_call_value + ): self.copy_expression( next_expr, true_expression.call_value, false_expression.call_value, ) + else: + return true_expression._arguments = [] false_expression._arguments = [] for next_expr in expression.arguments: - if self.apply_copy(next_expr, true_expression, false_expression, f_call): + if self.conditional_not_ahead(next_expr, true_expression, false_expression, f_call): # always on last arguments added self.copy_expression( next_expr, @@ -149,7 +187,9 @@ class SplitTernaryExpression: elif isinstance(expression, (TypeConversion, UnaryOperation)): next_expr = expression.expression - if self.apply_copy(next_expr, true_expression, false_expression, f_expression): + if self.conditional_not_ahead( + next_expr, true_expression, false_expression, f_expression + ): self.copy_expression( expression.expression, true_expression.expression, @@ -160,35 +200,3 @@ class SplitTernaryExpression: raise SlitherException( f"Ternary operation not handled {expression}({type(expression)})" ) - - -def _handle_ternary_access( - next_expr: IndexAccess, - true_expression: AssignmentOperation, - false_expression: AssignmentOperation, -): - """ - Conditional ternary accesses are split into two accesses, one true and one false - E.g. x[if cond ? 1 : 2] -> if cond { x[1] } else { x[2] } - """ - true_index_access = IndexAccess( - next_expr.expression_left, - next_expr.expression_right.then_expression, - next_expr.type, - ) - false_index_access = IndexAccess( - next_expr.expression_left, - next_expr.expression_right.else_expression, - next_expr.type, - ) - - f_expressions( - true_expression, - true_index_access, - ) - f_expressions( - false_expression, - false_index_access, - ) - - return next_expr.expression_right diff --git a/tests/slithir/ternary_expressions.sol b/tests/slithir/ternary_expressions.sol index c2e50b719..7fcc675c1 100644 --- a/tests/slithir/ternary_expressions.sol +++ b/tests/slithir/ternary_expressions.sol @@ -1,3 +1,6 @@ +interface NameReg { + function addressOf() external payable; +} contract C { // TODO // 1) support variable declarations @@ -21,4 +24,12 @@ contract C { function d(bool cond, bytes calldata x) external { bytes1 a = x[cond ? 1 : 2]; } + + function e(address one, address two) public { + return NameReg(one).addressOf{value: msg.sender == two ? 1 : 2, gas: true ? 2 : gasleft()}(); + } + // TODO: nested ternary + // function f(address one, address two) public { + // return NameReg(one).addressOf{value: msg.sender == two ? 1 : 2, gas: true ? (1 == 1 ? 1 : 2) : gasleft()}(); + // } } From a1a0abe17dbc0bc805f5b038fe673376f0d5f14d Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Mon, 12 Dec 2022 12:45:31 -0600 Subject: [PATCH 09/25] support parenthetical ternary expr and update tests --- slither/utils/expression_manipulations.py | 7 +++++++ tests/slithir/ternary_expressions.sol | 21 ++++++++++++++------- tests/slithir/test_ternary_expressions.py | 6 +++--- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/slither/utils/expression_manipulations.py b/slither/utils/expression_manipulations.py index 777c0c2a1..9ae01fde7 100644 --- a/slither/utils/expression_manipulations.py +++ b/slither/utils/expression_manipulations.py @@ -68,6 +68,13 @@ class SplitTernaryExpression: false_expression: Union[AssignmentOperation, MemberAccess], f: Callable, ) -> bool: + # parentetical expression (.. ? .. : ..) + if ( + isinstance(next_expr, TupleExpression) + and len(next_expr.expressions) == 1 + and isinstance(next_expr.expressions[0], ConditionalExpression) + ): + next_expr = next_expr.expressions[0] if isinstance(next_expr, ConditionalExpression): f(true_expression, copy.copy(next_expr.then_expression)) diff --git a/tests/slithir/ternary_expressions.sol b/tests/slithir/ternary_expressions.sol index 7fcc675c1..c6a6d4643 100644 --- a/tests/slithir/ternary_expressions.sol +++ b/tests/slithir/ternary_expressions.sol @@ -1,5 +1,6 @@ -interface NameReg { - function addressOf() external payable; +interface Test { + function test() external payable returns (uint); + function testTuple() external payable returns (uint, uint); } contract C { // TODO @@ -26,10 +27,16 @@ contract C { } function e(address one, address two) public { - return NameReg(one).addressOf{value: msg.sender == two ? 1 : 2, gas: true ? 2 : gasleft()}(); + uint x = Test(one).test{value: msg.sender == two ? 1 : 2, gas: true ? 2 : gasleft()}(); + } + + // Parenthteical expression + function f(address one, address two) public { + uint x = Test(one).test{value: msg.sender == two ? 1 : 2, gas: true ? (1 == 1 ? 1 : 2) : gasleft()}(); + } + + // Unused tuple variable + function g(address one) public { + (, uint x) = Test(one).testTuple(); } - // TODO: nested ternary - // function f(address one, address two) public { - // return NameReg(one).addressOf{value: msg.sender == two ? 1 : 2, gas: true ? (1 == 1 ? 1 : 2) : gasleft()}(); - // } } diff --git a/tests/slithir/test_ternary_expressions.py b/tests/slithir/test_ternary_expressions.py index db5658787..17cac6b2f 100644 --- a/tests/slithir/test_ternary_expressions.py +++ b/tests/slithir/test_ternary_expressions.py @@ -9,10 +9,10 @@ def test_ternary_conversions() -> None: slither = Slither("./tests/slithir/ternary_expressions.sol") for contract in slither.contracts: for function in contract.functions: + vars_declared = 0 + vars_assigned = 0 for node in function.nodes: if node.type in [NodeType.IF, NodeType.IFLOOP]: - vars_declared = 0 - vars_assigned = 0 # Iterate over true and false son for inner_node in node.sons: @@ -31,7 +31,7 @@ def test_ternary_conversions() -> None: if isinstance(ir, Assignment): vars_assigned += 1 - assert vars_declared == vars_assigned + assert vars_declared == vars_assigned if __name__ == "__main__": From a1343a8df596746999e766f8f7c51ed6a8ed93b4 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Mon, 12 Dec 2022 12:47:27 -0600 Subject: [PATCH 10/25] update function name --- slither/solc_parsing/declarations/modifier.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slither/solc_parsing/declarations/modifier.py b/slither/solc_parsing/declarations/modifier.py index a3f07da7f..e55487612 100644 --- a/slither/solc_parsing/declarations/modifier.py +++ b/slither/solc_parsing/declarations/modifier.py @@ -87,7 +87,7 @@ class ModifierSolc(FunctionSolc): for node in self._node_to_nodesolc.values(): node.analyze_expressions(self) - self._filter_ternary() + self._rewrite_ternary_as_if_else() self._remove_alone_endif() # self._analyze_read_write() From ca252f147293e648d748593c891f1742a9b07db5 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Mon, 12 Dec 2022 13:10:25 -0600 Subject: [PATCH 11/25] spelling and linting --- slither/utils/expression_manipulations.py | 6 +++--- tests/slithir/ternary_expressions.sol | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/slither/utils/expression_manipulations.py b/slither/utils/expression_manipulations.py index 9ae01fde7..591fab0ef 100644 --- a/slither/utils/expression_manipulations.py +++ b/slither/utils/expression_manipulations.py @@ -3,7 +3,7 @@ as they should be immutable """ import copy -from typing import Union, Callable, Tuple, Optional +from typing import Union, Callable from slither.core.expressions import UnaryOperation from slither.core.expressions.assignment_operation import AssignmentOperation from slither.core.expressions.binary_operation import BinaryOperation @@ -68,7 +68,7 @@ class SplitTernaryExpression: false_expression: Union[AssignmentOperation, MemberAccess], f: Callable, ) -> bool: - # parentetical expression (.. ? .. : ..) + # look ahead for parenthetical expression (.. ? .. : ..) if ( isinstance(next_expr, TupleExpression) and len(next_expr.expressions) == 1 @@ -112,7 +112,7 @@ class SplitTernaryExpression: self.copy_expression( next_expr, true_expression.expression, false_expression.expression ) - + # pylint: disable=too-many-nested-blocks elif isinstance(expression, (AssignmentOperation, BinaryOperation, TupleExpression)): true_expression._expressions = [] false_expression._expressions = [] diff --git a/tests/slithir/ternary_expressions.sol b/tests/slithir/ternary_expressions.sol index c6a6d4643..89fdc59d1 100644 --- a/tests/slithir/ternary_expressions.sol +++ b/tests/slithir/ternary_expressions.sol @@ -30,7 +30,7 @@ contract C { uint x = Test(one).test{value: msg.sender == two ? 1 : 2, gas: true ? 2 : gasleft()}(); } - // Parenthteical expression + // Parenthetical expression function f(address one, address two) public { uint x = Test(one).test{value: msg.sender == two ? 1 : 2, gas: true ? (1 == 1 ? 1 : 2) : gasleft()}(); } From 59b9b0392dfb243f73269967d57450f066f7267f Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Tue, 13 Dec 2022 13:46:00 -0600 Subject: [PATCH 12/25] analyze all inherited contracts' using for directives first --- .../slither_compilation_unit_solc.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/slither/solc_parsing/slither_compilation_unit_solc.py b/slither/solc_parsing/slither_compilation_unit_solc.py index f08a71487..67c0cb0fc 100644 --- a/slither/solc_parsing/slither_compilation_unit_solc.py +++ b/slither/solc_parsing/slither_compilation_unit_solc.py @@ -508,6 +508,7 @@ Please rename it, this name is reserved for Slither's internals""" # Then we analyse state variables, functions and modifiers self._analyze_third_part(contracts_to_be_analyzed, libraries) + [c.set_is_analyzed(False) for c in self._underlying_contract_to_parser.values()] self._analyze_using_for(contracts_to_be_analyzed) @@ -624,8 +625,20 @@ Please rename it, this name is reserved for Slither's internals""" def _analyze_using_for(self, contracts_to_be_analyzed: List[ContractSolc]): self._analyze_top_level_using_for() - for c in contracts_to_be_analyzed: - c.analyze_using_for() + while contracts_to_be_analyzed: + contract = contracts_to_be_analyzed[0] + + contracts_to_be_analyzed = contracts_to_be_analyzed[1:] + all_father_analyzed = all( + self._underlying_contract_to_parser[father].is_analyzed + for father in contract.underlying_contract.inheritance + ) + + if not contract.underlying_contract.inheritance or all_father_analyzed: + contract.analyze_using_for() + contract.set_is_analyzed(True) + else: + contracts_to_be_analyzed += [contract] def _analyze_enums(self, contract: ContractSolc): # Enum must be analyzed first @@ -711,6 +724,7 @@ Please rename it, this name is reserved for Slither's internals""" for func in contract.functions + contract.modifiers: try: func.generate_slithir_and_analyze() + except AttributeError as e: # This can happens for example if there is a call to an interface # And the interface is redefined due to contract's name reuse From 2d41b962a9d2441c228b75becc11e5f3ee47781f Mon Sep 17 00:00:00 2001 From: Simone Date: Tue, 13 Dec 2022 22:06:56 +0100 Subject: [PATCH 13/25] rename FileScope.usingFor to using_for_directives --- slither/core/declarations/contract.py | 2 +- slither/core/scope/scope.py | 6 +++--- slither/solc_parsing/declarations/using_for_top_level.py | 6 +++--- slither/solc_parsing/slither_compilation_unit_solc.py | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/slither/core/declarations/contract.py b/slither/core/declarations/contract.py index 1637ea622..1262877f9 100644 --- a/slither/core/declarations/contract.py +++ b/slither/core/declarations/contract.py @@ -281,7 +281,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods if self._using_for_complete is None: result = self.using_for - top_level_using_for = self.file_scope.usingFor + top_level_using_for = self.file_scope.using_for_directives for uftl in top_level_using_for: result = _merge_using_for(result, uftl.using_for) self._using_for_complete = result diff --git a/slither/core/scope/scope.py b/slither/core/scope/scope.py index a27483824..4260e3a5e 100644 --- a/slither/core/scope/scope.py +++ b/slither/core/scope/scope.py @@ -36,7 +36,7 @@ class FileScope: # Because we parse the function signature later on # So we simplify the logic and have the scope fields all populated self.functions: Set[FunctionTopLevel] = set() - self.usingFor: Set[UsingForTopLevel] = set() + self.using_for_directives: Set[UsingForTopLevel] = set() self.imports: Set[Import] = set() self.pragmas: Set[Pragma] = set() self.structures: Dict[str, StructureTopLevel] = {} @@ -74,8 +74,8 @@ class FileScope: if not new_scope.functions.issubset(self.functions): self.functions |= new_scope.functions learn_something = True - if not new_scope.usingFor.issubset(self.usingFor): - self.usingFor |= new_scope.usingFor + if not new_scope.using_for_directives.issubset(self.using_for_directives): + self.using_for_directives |= new_scope.using_for_directives learn_something = True if not new_scope.imports.issubset(self.imports): self.imports |= new_scope.imports diff --git a/slither/solc_parsing/declarations/using_for_top_level.py b/slither/solc_parsing/declarations/using_for_top_level.py index 89d1dc276..85b09130d 100644 --- a/slither/solc_parsing/declarations/using_for_top_level.py +++ b/slither/solc_parsing/declarations/using_for_top_level.py @@ -107,7 +107,7 @@ class UsingForTopLevelSolc(CallerContextExpression): # pylint: disable=too-few- if isinstance(type_name, TypeAliasTopLevel): for alias in scope.user_defined_types.values(): if alias == type_name: - scope.usingFor.add(self._using_for) + scope.using_for_directives.add(self._using_for) elif isinstance(type_name, UserDefinedType): self._propagate_global_UserDefinedType(scope, type_name) else: @@ -122,11 +122,11 @@ class UsingForTopLevelSolc(CallerContextExpression): # pylint: disable=too-few- if isinstance(underlying, StructureTopLevel): for struct in scope.structures.values(): if struct == underlying: - scope.usingFor.add(self._using_for) + scope.using_for_directives.add(self._using_for) elif isinstance(underlying, EnumTopLevel): for enum in scope.enums.values(): if enum == underlying: - scope.usingFor.add(self._using_for) + scope.using_for_directives.add(self._using_for) else: LOGGER.error( f"Error when propagating global {underlying} {type(underlying)} not a StructTopLevel or EnumTopLevel" diff --git a/slither/solc_parsing/slither_compilation_unit_solc.py b/slither/solc_parsing/slither_compilation_unit_solc.py index b2d76052b..78b6d4733 100644 --- a/slither/solc_parsing/slither_compilation_unit_solc.py +++ b/slither/solc_parsing/slither_compilation_unit_solc.py @@ -230,7 +230,7 @@ class SlitherCompilationUnitSolc: usingFor = UsingForTopLevel(scope) usingFor_parser = UsingForTopLevelSolc(usingFor, top_level_data, self) usingFor.set_offset(top_level_data["src"], self._compilation_unit) - scope.usingFor.add(usingFor) + scope.using_for_directives.add(usingFor) self._compilation_unit.using_for_top_level.append(usingFor) self._using_for_top_level_parser.append(usingFor_parser) From 9c339a69299bbcb4becadf8fb84f27f3c275e1f8 Mon Sep 17 00:00:00 2001 From: webthethird Date: Fri, 23 Dec 2022 13:28:40 -0600 Subject: [PATCH 14/25] Implement `--detect` and `--exclude` for slither-check-upgradeability --- slither/tools/upgradeability/__main__.py | 61 ++++++++++++++++++++---- 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/slither/tools/upgradeability/__main__.py b/slither/tools/upgradeability/__main__.py index 414a4c175..bafe3fa9e 100644 --- a/slither/tools/upgradeability/__main__.py +++ b/slither/tools/upgradeability/__main__.py @@ -27,7 +27,9 @@ logger: logging.Logger = logging.getLogger("Slither") logger.setLevel(logging.INFO) -def parse_args() -> argparse.Namespace: +def parse_args( + check_classes: List[Type[AbstractCheck]] +) -> argparse.Namespace: parser = argparse.ArgumentParser( description="Slither Upgradeability Checks. For usage information see https://github.com/crytic/slither/wiki/Upgradeability-Checks.", usage="slither-check-upgradeability contract.sol ContractName", @@ -51,6 +53,23 @@ def parse_args() -> argparse.Namespace: default=False, ) + parser.add_argument( + "--detect", + help="Comma-separated list of detectors, defaults to all, " + f"available detectors: {', '.join(d.ARGUMENT for d in check_classes)}", + action="store", + dest="detectors_to_run", + default="all", + ) + + parser.add_argument( + "--exclude", + help="Comma-separated list of detectors that should be excluded", + action="store", + dest="detectors_to_exclude", + default=None, + ) + parser.add_argument( "--list-detectors", help="List available detectors", @@ -104,6 +123,30 @@ def _get_checks() -> List[Type[AbstractCheck]]: return detectors +def choose_checks( + args: argparse.Namespace, all_check_classes: List[Type[AbstractCheck]] +) -> List[Type[AbstractCheck]]: + detectors_to_run = [] + detectors = {d.ARGUMENT: d for d in all_check_classes} + + if args.detectors_to_run == "all": + detectors_to_run = all_check_classes + if args.detectors_to_exclude: + detectors_excluded = args.detectors_to_exclude.split(",") + for detector in detectors: + if detector in detectors_excluded: + detectors_to_run.remove(detectors[detector]) + else: + for detector in args.detectors_to_run.split(","): + if detector in detectors: + detectors_to_run.append(detectors[detector]) + else: + raise Exception(f"Error: {detector} is not a detector") + detectors_to_run = sorted(detectors_to_run, key=lambda x: x.IMPACT) + return detectors_to_run + return detectors_to_run + + class ListDetectors(argparse.Action): # pylint: disable=too-few-public-methods def __call__( self, parser: Any, *args: Any, **kwargs: Any @@ -200,11 +243,11 @@ def main() -> None: "detectors": [], } - args = parse_args() - + detectors = _get_checks() + args = parse_args(detectors) + detectors_to_run = choose_checks(args, detectors) v1_filename = vars(args)["contract.sol"] number_detectors_run = 0 - detectors = _get_checks() try: variable1 = Slither(v1_filename, **vars(args)) @@ -219,7 +262,7 @@ def main() -> None: return v1_contract = v1_contracts[0] - detectors_results, number_detectors = _checks_on_contract(detectors, v1_contract) + detectors_results, number_detectors = _checks_on_contract(detectors_to_run, v1_contract) json_results["detectors"] += detectors_results number_detectors_run += number_detectors @@ -242,7 +285,7 @@ def main() -> None: json_results["proxy-present"] = True detectors_results, number_detectors = _checks_on_contract_and_proxy( - detectors, v1_contract, proxy_contract + detectors_to_run, v1_contract, proxy_contract ) json_results["detectors"] += detectors_results number_detectors_run += number_detectors @@ -267,19 +310,19 @@ def main() -> None: if proxy_contract: detectors_results, _ = _checks_on_contract_and_proxy( - detectors, v2_contract, proxy_contract + detectors_to_run, v2_contract, proxy_contract ) json_results["detectors"] += detectors_results detectors_results, number_detectors = _checks_on_contract_update( - detectors, v1_contract, v2_contract + detectors_to_run, v1_contract, v2_contract ) json_results["detectors"] += detectors_results number_detectors_run += number_detectors # If there is a V2, we run the contract-only check on the V2 - detectors_results, number_detectors = _checks_on_contract(detectors, v2_contract) + detectors_results, number_detectors = _checks_on_contract(detectors_to_run, v2_contract) json_results["detectors"] += detectors_results number_detectors_run += number_detectors From 194b1bd90563092dbf97d254dda4ad253cda91fe Mon Sep 17 00:00:00 2001 From: webthethird Date: Fri, 23 Dec 2022 13:41:37 -0600 Subject: [PATCH 15/25] Implement `--exclude-` for slither-check-upgradeability --- slither/tools/upgradeability/__main__.py | 63 +++++++++++++++++++++--- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/slither/tools/upgradeability/__main__.py b/slither/tools/upgradeability/__main__.py index bafe3fa9e..f71ed1e5b 100644 --- a/slither/tools/upgradeability/__main__.py +++ b/slither/tools/upgradeability/__main__.py @@ -35,6 +35,8 @@ def parse_args( usage="slither-check-upgradeability contract.sol ContractName", ) + group_checks = parser.add_argument_group("Checks") + parser.add_argument("contract.sol", help="Codebase to analyze") parser.add_argument("ContractName", help="Contract name (logic contract)") @@ -53,7 +55,7 @@ def parse_args( default=False, ) - parser.add_argument( + group_checks.add_argument( "--detect", help="Comma-separated list of detectors, defaults to all, " f"available detectors: {', '.join(d.ARGUMENT for d in check_classes)}", @@ -62,7 +64,15 @@ def parse_args( default="all", ) - parser.add_argument( + group_checks.add_argument( + "--list-detectors", + help="List available detectors", + action=ListDetectors, + nargs=0, + default=False, + ) + + group_checks.add_argument( "--exclude", help="Comma-separated list of detectors that should be excluded", action="store", @@ -70,11 +80,31 @@ def parse_args( default=None, ) - parser.add_argument( - "--list-detectors", - help="List available detectors", - action=ListDetectors, - nargs=0, + group_checks.add_argument( + "--exclude-informational", + help="Exclude informational impact analyses", + action="store_true", + default=False, + ) + + group_checks.add_argument( + "--exclude-low", + help="Exclude low impact analyses", + action="store_true", + default=False, + ) + + group_checks.add_argument( + "--exclude-medium", + help="Exclude medium impact analyses", + action="store_true", + default=False, + ) + + group_checks.add_argument( + "--exclude-high", + help="Exclude high impact analyses", + action="store_true", default=False, ) @@ -144,6 +174,25 @@ def choose_checks( raise Exception(f"Error: {detector} is not a detector") detectors_to_run = sorted(detectors_to_run, key=lambda x: x.IMPACT) return detectors_to_run + + if args.exclude_informational: + detectors_to_run = [ + d for d in detectors_to_run if d.IMPACT != DetectorClassification.INFORMATIONAL + ] + if args.exclude_low: + detectors_to_run = [ + d for d in detectors_to_run if d.IMPACT != DetectorClassification.LOW + ] + if args.exclude_medium: + detectors_to_run = [ + d for d in detectors_to_run if d.IMPACT != DetectorClassification.MEDIUM + ] + if args.exclude_high: + detectors_to_run = [ + d for d in detectors_to_run if d.IMPACT != DetectorClassification.HIGH + ] + + detectors_to_run = sorted(detectors_to_run, key=lambda x: x.IMPACT) return detectors_to_run From c66f2dca4069b5e83b5e8e6f6820cf35db96f56e Mon Sep 17 00:00:00 2001 From: webthethird Date: Fri, 23 Dec 2022 13:51:04 -0600 Subject: [PATCH 16/25] Import `CheckClassification` and fix references --- slither/tools/upgradeability/__main__.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/slither/tools/upgradeability/__main__.py b/slither/tools/upgradeability/__main__.py index f71ed1e5b..7a5beacf5 100644 --- a/slither/tools/upgradeability/__main__.py +++ b/slither/tools/upgradeability/__main__.py @@ -14,7 +14,10 @@ from slither.exceptions import SlitherException from slither.utils.colors import red from slither.utils.output import output_to_json from slither.tools.upgradeability.checks import all_checks -from slither.tools.upgradeability.checks.abstract_checks import AbstractCheck +from slither.tools.upgradeability.checks.abstract_checks import ( + AbstractCheck, + CheckClassification, +) from slither.tools.upgradeability.utils.command_line import ( output_detectors_json, output_wiki, @@ -177,19 +180,19 @@ def choose_checks( if args.exclude_informational: detectors_to_run = [ - d for d in detectors_to_run if d.IMPACT != DetectorClassification.INFORMATIONAL + d for d in detectors_to_run if d.IMPACT != CheckClassification.INFORMATIONAL ] if args.exclude_low: detectors_to_run = [ - d for d in detectors_to_run if d.IMPACT != DetectorClassification.LOW + d for d in detectors_to_run if d.IMPACT != CheckClassification.LOW ] if args.exclude_medium: detectors_to_run = [ - d for d in detectors_to_run if d.IMPACT != DetectorClassification.MEDIUM + d for d in detectors_to_run if d.IMPACT != CheckClassification.MEDIUM ] if args.exclude_high: detectors_to_run = [ - d for d in detectors_to_run if d.IMPACT != DetectorClassification.HIGH + d for d in detectors_to_run if d.IMPACT != CheckClassification.HIGH ] detectors_to_run = sorted(detectors_to_run, key=lambda x: x.IMPACT) From 1965d262b1f9255a9bb17e4546d74b0af19d572b Mon Sep 17 00:00:00 2001 From: webthethird Date: Fri, 23 Dec 2022 13:52:22 -0600 Subject: [PATCH 17/25] Black --- slither/tools/upgradeability/__main__.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/slither/tools/upgradeability/__main__.py b/slither/tools/upgradeability/__main__.py index 7a5beacf5..ceb9ce421 100644 --- a/slither/tools/upgradeability/__main__.py +++ b/slither/tools/upgradeability/__main__.py @@ -30,9 +30,7 @@ logger: logging.Logger = logging.getLogger("Slither") logger.setLevel(logging.INFO) -def parse_args( - check_classes: List[Type[AbstractCheck]] -) -> argparse.Namespace: +def parse_args(check_classes: List[Type[AbstractCheck]]) -> argparse.Namespace: parser = argparse.ArgumentParser( description="Slither Upgradeability Checks. For usage information see https://github.com/crytic/slither/wiki/Upgradeability-Checks.", usage="slither-check-upgradeability contract.sol ContractName", @@ -183,17 +181,11 @@ def choose_checks( d for d in detectors_to_run if d.IMPACT != CheckClassification.INFORMATIONAL ] if args.exclude_low: - detectors_to_run = [ - d for d in detectors_to_run if d.IMPACT != CheckClassification.LOW - ] + detectors_to_run = [d for d in detectors_to_run if d.IMPACT != CheckClassification.LOW] if args.exclude_medium: - detectors_to_run = [ - d for d in detectors_to_run if d.IMPACT != CheckClassification.MEDIUM - ] + detectors_to_run = [d for d in detectors_to_run if d.IMPACT != CheckClassification.MEDIUM] if args.exclude_high: - detectors_to_run = [ - d for d in detectors_to_run if d.IMPACT != CheckClassification.HIGH - ] + detectors_to_run = [d for d in detectors_to_run if d.IMPACT != CheckClassification.HIGH] detectors_to_run = sorted(detectors_to_run, key=lambda x: x.IMPACT) return detectors_to_run From cd8c6388e7e2e1865a590caae9707f39ec9b0eae Mon Sep 17 00:00:00 2001 From: webthethird Date: Fri, 23 Dec 2022 13:58:55 -0600 Subject: [PATCH 18/25] Don't sort checks by impact which caused CI test to fail --- slither/tools/upgradeability/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slither/tools/upgradeability/__main__.py b/slither/tools/upgradeability/__main__.py index ceb9ce421..56b838b9c 100644 --- a/slither/tools/upgradeability/__main__.py +++ b/slither/tools/upgradeability/__main__.py @@ -187,7 +187,7 @@ def choose_checks( if args.exclude_high: detectors_to_run = [d for d in detectors_to_run if d.IMPACT != CheckClassification.HIGH] - detectors_to_run = sorted(detectors_to_run, key=lambda x: x.IMPACT) + # detectors_to_run = sorted(detectors_to_run, key=lambda x: x.IMPACT) return detectors_to_run From baf4143345059339719a12960283eed6e6490f6c Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Tue, 3 Jan 2023 12:03:29 -0600 Subject: [PATCH 19/25] move nested logic into functions --- slither/utils/expression_manipulations.py | 178 +++++++++++----------- 1 file changed, 92 insertions(+), 86 deletions(-) diff --git a/slither/utils/expression_manipulations.py b/slither/utils/expression_manipulations.py index 591fab0ef..974c6f68b 100644 --- a/slither/utils/expression_manipulations.py +++ b/slither/utils/expression_manipulations.py @@ -23,7 +23,8 @@ from slither.all_exceptions import SlitherException # pylint: disable=protected-access def f_expressions( - e: AssignmentOperation, x: Union[Identifier, Literal, MemberAccess, IndexAccess] + e: Union[AssignmentOperation, BinaryOperation, TupleExpression], + x: Union[Identifier, Literal, MemberAccess, IndexAccess], ) -> None: e._expressions.append(x) @@ -40,7 +41,7 @@ def f_call_gas(e: CallExpression, x): e._gas = x -def f_expression(e, x): +def f_expression(e: Union[TypeConversion, UnaryOperation, MemberAccess], x): e._expression = x @@ -86,7 +87,6 @@ class SplitTernaryExpression: f(false_expression, copy.copy(next_expr)) return True - # pylint: disable=too-many-branches def copy_expression( self, expression: Expression, true_expression: Expression, false_expression: Expression ) -> None: @@ -102,108 +102,114 @@ class SplitTernaryExpression: ): return - # case of lib - # (.. ? .. : ..).add - if isinstance(expression, MemberAccess): - next_expr = expression.expression - if self.conditional_not_ahead( - next_expr, true_expression, false_expression, f_expression - ): - self.copy_expression( - next_expr, true_expression.expression, false_expression.expression - ) - # pylint: disable=too-many-nested-blocks elif isinstance(expression, (AssignmentOperation, BinaryOperation, TupleExpression)): true_expression._expressions = [] false_expression._expressions = [] - for next_expr in expression.expressions: - # TODO: can we get rid of `NoneType` expressions in `TupleExpression`? - if next_expr: - if isinstance(next_expr, IndexAccess): - # create an index access for each branch - # x[if cond ? 1 : 2] -> if cond { x[1] } else { x[2] } - for expr in next_expr.expressions: - if self.conditional_not_ahead( - expr, true_expression, false_expression, f_expressions - ): - self.copy_expression( - expr, - true_expression.expressions[-1], - false_expression.expressions[-1], - ) - - if self.conditional_not_ahead( - next_expr, true_expression, false_expression, f_expressions - ): - # always on last arguments added - self.copy_expression( - next_expr, - true_expression.expressions[-1], - false_expression.expressions[-1], - ) + self.convert_expressions(expression, true_expression, false_expression) elif isinstance(expression, CallExpression): next_expr = expression.called + self.convert_call_expression(expression, next_expr, true_expression, false_expression) + + elif isinstance(expression, (TypeConversion, UnaryOperation, MemberAccess)): + next_expr = expression.expression + if self.conditional_not_ahead( + next_expr, true_expression, false_expression, f_expression + ): + self.copy_expression( + expression.expression, + true_expression.expression, + false_expression.expression, + ) - # case of lib - # (.. ? .. : ..).add - if self.conditional_not_ahead(next_expr, true_expression, false_expression, f_called): - self.copy_expression(next_expr, true_expression.called, false_expression.called) + else: + raise SlitherException( + f"Ternary operation not handled {expression}({type(expression)})" + ) - # In order to handle ternaries in both call options, gas and value, we return early if the - # conditional is not ahead to rewrite both ternaries (see `_rewrite_ternary_as_if_else`). - if expression.call_gas: - # case of (..).func{gas: .. ? .. : ..}() - next_expr = expression.call_gas - if self.conditional_not_ahead( - next_expr, true_expression, false_expression, f_call_gas - ): - self.copy_expression( - next_expr, - true_expression.call_gas, - false_expression.call_gas, - ) - else: - return + def convert_expressions( + self, + expression: Union[AssignmentOperation, BinaryOperation, TupleExpression], + true_expression: Expression, + false_expression: Expression, + ) -> None: + for next_expr in expression.expressions: + # TODO: can we get rid of `NoneType` expressions in `TupleExpression`? + if next_expr: + if isinstance(next_expr, IndexAccess): + self.convert_index_access(next_expr, true_expression, false_expression) - if expression.call_value: - # case of (..).func{value: .. ? .. : ..}() - next_expr = expression.call_value if self.conditional_not_ahead( - next_expr, true_expression, false_expression, f_call_value + next_expr, true_expression, false_expression, f_expressions ): + # always on last arguments added self.copy_expression( next_expr, - true_expression.call_value, - false_expression.call_value, + true_expression.expressions[-1], + false_expression.expressions[-1], ) - else: - return - true_expression._arguments = [] - false_expression._arguments = [] + def convert_index_access( + self, next_expr: IndexAccess, true_expression: Expression, false_expression: Expression + ) -> None: + # create an index access for each branch + # x[if cond ? 1 : 2] -> if cond { x[1] } else { x[2] } + for expr in next_expr.expressions: + if self.conditional_not_ahead(expr, true_expression, false_expression, f_expressions): + self.copy_expression( + expr, + true_expression.expressions[-1], + false_expression.expressions[-1], + ) - for next_expr in expression.arguments: - if self.conditional_not_ahead(next_expr, true_expression, false_expression, f_call): - # always on last arguments added - self.copy_expression( - next_expr, - true_expression.arguments[-1], - false_expression.arguments[-1], - ) + def convert_call_expression( + self, + expression: CallExpression, + next_expr: Expression, + true_expression: Expression, + false_expression: Expression, + ) -> None: + # case of lib + # (.. ? .. : ..).add + if self.conditional_not_ahead(next_expr, true_expression, false_expression, f_called): + self.copy_expression(next_expr, true_expression.called, false_expression.called) + + # In order to handle ternaries in both call options, gas and value, we return early if the + # conditional is not ahead to rewrite both ternaries (see `_rewrite_ternary_as_if_else`). + if expression.call_gas: + # case of (..).func{gas: .. ? .. : ..}() + next_expr = expression.call_gas + if self.conditional_not_ahead(next_expr, true_expression, false_expression, f_call_gas): + self.copy_expression( + next_expr, + true_expression.call_gas, + false_expression.call_gas, + ) + else: + return - elif isinstance(expression, (TypeConversion, UnaryOperation)): - next_expr = expression.expression + if expression.call_value: + # case of (..).func{value: .. ? .. : ..}() + next_expr = expression.call_value if self.conditional_not_ahead( - next_expr, true_expression, false_expression, f_expression + next_expr, true_expression, false_expression, f_call_value ): self.copy_expression( - expression.expression, - true_expression.expression, - false_expression.expression, + next_expr, + true_expression.call_value, + false_expression.call_value, ) + else: + return - else: - raise SlitherException( - f"Ternary operation not handled {expression}({type(expression)})" - ) + true_expression._arguments = [] + false_expression._arguments = [] + + for expr in expression.arguments: + if self.conditional_not_ahead(expr, true_expression, false_expression, f_call): + # always on last arguments added + self.copy_expression( + expr, + true_expression.arguments[-1], + false_expression.arguments[-1], + ) From b68f4c17a7aedfe0382828b85e2ecd95dca4fb38 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Tue, 3 Jan 2023 12:31:47 -0600 Subject: [PATCH 20/25] pylint --- slither/utils/expression_manipulations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slither/utils/expression_manipulations.py b/slither/utils/expression_manipulations.py index 974c6f68b..bc2a1556e 100644 --- a/slither/utils/expression_manipulations.py +++ b/slither/utils/expression_manipulations.py @@ -102,7 +102,7 @@ class SplitTernaryExpression: ): return - elif isinstance(expression, (AssignmentOperation, BinaryOperation, TupleExpression)): + if isinstance(expression, (AssignmentOperation, BinaryOperation, TupleExpression)): true_expression._expressions = [] false_expression._expressions = [] self.convert_expressions(expression, true_expression, false_expression) From 254f02b374d906df619edebe099af8f61eaef6e0 Mon Sep 17 00:00:00 2001 From: Feist Josselin Date: Thu, 5 Jan 2023 11:02:43 +0100 Subject: [PATCH 21/25] Update expression_manipulations.py --- slither/utils/expression_manipulations.py | 1 + 1 file changed, 1 insertion(+) diff --git a/slither/utils/expression_manipulations.py b/slither/utils/expression_manipulations.py index bc2a1556e..a63db9829 100644 --- a/slither/utils/expression_manipulations.py +++ b/slither/utils/expression_manipulations.py @@ -135,6 +135,7 @@ class SplitTernaryExpression: ) -> None: for next_expr in expression.expressions: # TODO: can we get rid of `NoneType` expressions in `TupleExpression`? + # montyly: this might happen with unnamed tuple (ex: (,,,) = f()), but it needs to be checked if next_expr: if isinstance(next_expr, IndexAccess): self.convert_index_access(next_expr, true_expression, false_expression) From 75ee80c3880082bbe32d6f9363bae837144a709c Mon Sep 17 00:00:00 2001 From: Simone Date: Thu, 5 Jan 2023 13:43:39 +0100 Subject: [PATCH 22/25] Fix edge case + code quality --- slither/slithir/convert.py | 47 ++++++++++--------- .../declarations/using_for_top_level.py | 7 +-- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/slither/slithir/convert.py b/slither/slithir/convert.py index 82b659981..445fc7a8e 100644 --- a/slither/slithir/convert.py +++ b/slither/slithir/convert.py @@ -201,18 +201,19 @@ def _fits_under_byte(val: Union[int, str]) -> List[str]: return [f"bytes{f}" for f in range(length, 33)] + ["bytes"] -def _find_function_from_parameter(ir: Call, candidates: List[Function]) -> Optional[Function]: +def _find_function_from_parameter( + arguments: List[Variable], candidates: List[Function] +) -> Optional[Function]: """ - Look for a function in candidates that can be the target of the ir's call + Look for a function in candidates that can be the target based on the ir's call arguments Try the implicit type conversion for uint/int/bytes. Constant values can be both uint/int While variables stick to their base type, but can changed the size - :param ir: + :param arguments: :param candidates: :return: """ - arguments = ir.arguments type_args: List[str] for idx, arg in enumerate(arguments): if isinstance(arg, (list,)): @@ -1335,20 +1336,24 @@ def convert_to_pop(ir, node): def look_for_library_or_top_level(contract, ir, using_for, t): for destination in using_for[t]: if isinstance(destination, FunctionTopLevel) and destination.name == ir.function_name: - internalcall = InternalCall(destination, ir.nbr_arguments, ir.lvalue, ir.type_call) - internalcall.set_expression(ir.expression) - internalcall.set_node(ir.node) - internalcall.call_gas = ir.call_gas - internalcall.arguments = [ir.destination] + ir.arguments - return_type = internalcall.function.return_type - if return_type: - if len(return_type) == 1: - internalcall.lvalue.set_type(return_type[0]) - elif len(return_type) > 1: - internalcall.lvalue.set_type(return_type) - else: - internalcall.lvalue = None - return internalcall + arguments = [ir.destination] + ir.arguments + if ( + len(destination.parameters) == len(arguments) + and _find_function_from_parameter(arguments, [destination]) is not None + ): + internalcall = InternalCall(destination, ir.nbr_arguments, ir.lvalue, ir.type_call) + internalcall.set_expression(ir.expression) + internalcall.set_node(ir.node) + internalcall.arguments = [ir.destination] + ir.arguments + return_type = internalcall.function.return_type + if return_type: + if len(return_type) == 1: + internalcall.lvalue.set_type(return_type[0]) + elif len(return_type) > 1: + internalcall.lvalue.set_type(return_type) + else: + internalcall.lvalue = None + return internalcall if isinstance(destination, FunctionContract) and destination.contract.is_library: lib_contract = destination.contract @@ -1431,7 +1436,7 @@ def convert_type_library_call(ir: HighLevelCall, lib_contract: Contract): # TODO: handle collision with multiple state variables/functions func = lib_contract.get_state_variable_from_name(ir.function_name) if func is None and candidates: - func = _find_function_from_parameter(ir, candidates) + func = _find_function_from_parameter(ir.arguments, candidates) # In case of multiple binding to the same type # TODO: this part might not be needed with _find_function_from_parameter @@ -1527,7 +1532,7 @@ def convert_type_of_high_and_internal_level_call(ir: Operation, contract: Option if f.name == ir.function_name and len(f.parameters) == len(ir.arguments) ] - func = _find_function_from_parameter(ir, candidates) + func = _find_function_from_parameter(ir.arguments, candidates) if not func: assert contract @@ -1550,7 +1555,7 @@ def convert_type_of_high_and_internal_level_call(ir: Operation, contract: Option # TODO: handle collision with multiple state variables/functions func = contract.get_state_variable_from_name(ir.function_name) if func is None and candidates: - func = _find_function_from_parameter(ir, candidates) + func = _find_function_from_parameter(ir.arguments, candidates) # lowlelvel lookup needs to be done at last step if not func: diff --git a/slither/solc_parsing/declarations/using_for_top_level.py b/slither/solc_parsing/declarations/using_for_top_level.py index 85b09130d..3ec191d46 100644 --- a/slither/solc_parsing/declarations/using_for_top_level.py +++ b/slither/solc_parsing/declarations/using_for_top_level.py @@ -27,12 +27,12 @@ class UsingForTopLevelSolc(CallerContextExpression): # pylint: disable=too-few- UsingFor class """ - def __init__( # pylint: disable=too-many-arguments + def __init__( self, uftl: UsingForTopLevel, top_level_data: Dict, slither_parser: "SlitherCompilationUnitSolc", - ): + ) -> None: self._type_name = top_level_data["typeName"] self._global = top_level_data["global"] @@ -40,6 +40,7 @@ class UsingForTopLevelSolc(CallerContextExpression): # pylint: disable=too-few- self._library_name = top_level_data["libraryName"] else: self._functions = top_level_data["functionList"] + self._library_name = None self._using_for = uftl self._slither_parser = slither_parser @@ -48,7 +49,7 @@ class UsingForTopLevelSolc(CallerContextExpression): # pylint: disable=too-few- type_name = parse_type(self._type_name, self) self._using_for.using_for[type_name] = [] - if hasattr(self, "_library_name"): + if self._library_name is not None: library_name = parse_type(self._library_name, self) self._using_for.using_for[type_name].append(library_name) self._propagate_global(type_name) From 1c869df9e91f65143d4201511143c6f582b697f8 Mon Sep 17 00:00:00 2001 From: Simone Date: Thu, 5 Jan 2023 16:41:51 +0100 Subject: [PATCH 23/25] Fix implicit conversion --- slither/slithir/convert.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/slither/slithir/convert.py b/slither/slithir/convert.py index 445fc7a8e..1918d8cd2 100644 --- a/slither/slithir/convert.py +++ b/slither/slithir/convert.py @@ -202,16 +202,19 @@ def _fits_under_byte(val: Union[int, str]) -> List[str]: def _find_function_from_parameter( - arguments: List[Variable], candidates: List[Function] + arguments: List[Variable], candidates: List[Function], full_comparison: bool ) -> Optional[Function]: """ Look for a function in candidates that can be the target based on the ir's call arguments Try the implicit type conversion for uint/int/bytes. Constant values can be both uint/int - While variables stick to their base type, but can changed the size + While variables stick to their base type, but can changed the size. + If full_comparison is True it will do a comparison of all the arguments regardless if + the candidate remained is one. :param arguments: :param candidates: + :param full_comparison: :return: """ type_args: List[str] @@ -261,7 +264,7 @@ def _find_function_from_parameter( not_found = False candidates_kept.append(candidate) - if len(candidates_kept) == 1: + if len(candidates_kept) == 1 and not full_comparison: return candidates_kept[0] candidates = candidates_kept if len(candidates) == 1: @@ -1339,7 +1342,7 @@ def look_for_library_or_top_level(contract, ir, using_for, t): arguments = [ir.destination] + ir.arguments if ( len(destination.parameters) == len(arguments) - and _find_function_from_parameter(arguments, [destination]) is not None + and _find_function_from_parameter(arguments, [destination], True) is not None ): internalcall = InternalCall(destination, ir.nbr_arguments, ir.lvalue, ir.type_call) internalcall.set_expression(ir.expression) @@ -1436,7 +1439,7 @@ def convert_type_library_call(ir: HighLevelCall, lib_contract: Contract): # TODO: handle collision with multiple state variables/functions func = lib_contract.get_state_variable_from_name(ir.function_name) if func is None and candidates: - func = _find_function_from_parameter(ir.arguments, candidates) + func = _find_function_from_parameter(ir.arguments, candidates, False) # In case of multiple binding to the same type # TODO: this part might not be needed with _find_function_from_parameter @@ -1532,7 +1535,7 @@ def convert_type_of_high_and_internal_level_call(ir: Operation, contract: Option if f.name == ir.function_name and len(f.parameters) == len(ir.arguments) ] - func = _find_function_from_parameter(ir.arguments, candidates) + func = _find_function_from_parameter(ir.arguments, candidates, False) if not func: assert contract @@ -1555,7 +1558,7 @@ def convert_type_of_high_and_internal_level_call(ir: Operation, contract: Option # TODO: handle collision with multiple state variables/functions func = contract.get_state_variable_from_name(ir.function_name) if func is None and candidates: - func = _find_function_from_parameter(ir.arguments, candidates) + func = _find_function_from_parameter(ir.arguments, candidates, False) # lowlelvel lookup needs to be done at last step if not func: From 2ee6d0a4c8f31d27588738fdc9addb64b4b0854c Mon Sep 17 00:00:00 2001 From: Simone Date: Thu, 5 Jan 2023 16:44:30 +0100 Subject: [PATCH 24/25] Add tests --- .../using-for-3-0.8.0.sol-0.8.15-compact.zip | Bin 0 -> 4063 bytes .../using-for-4-0.8.0.sol-0.8.15-compact.zip | Bin 0 -> 4106 bytes .../using-for-3-0.8.0.sol-0.8.15-compact.json | 8 ++++++ .../using-for-4-0.8.0.sol-0.8.15-compact.json | 8 ++++++ tests/ast-parsing/using-for-3-0.8.0.sol | 27 ++++++++++++++++++ tests/ast-parsing/using-for-4-0.8.0.sol | 25 ++++++++++++++++ tests/test_ast_parsing.py | 2 ++ tests/test_features.py | 21 ++++++++++++++ 8 files changed, 91 insertions(+) create mode 100644 tests/ast-parsing/compile/using-for-3-0.8.0.sol-0.8.15-compact.zip create mode 100644 tests/ast-parsing/compile/using-for-4-0.8.0.sol-0.8.15-compact.zip create mode 100644 tests/ast-parsing/expected/using-for-3-0.8.0.sol-0.8.15-compact.json create mode 100644 tests/ast-parsing/expected/using-for-4-0.8.0.sol-0.8.15-compact.json create mode 100644 tests/ast-parsing/using-for-3-0.8.0.sol create mode 100644 tests/ast-parsing/using-for-4-0.8.0.sol diff --git a/tests/ast-parsing/compile/using-for-3-0.8.0.sol-0.8.15-compact.zip b/tests/ast-parsing/compile/using-for-3-0.8.0.sol-0.8.15-compact.zip new file mode 100644 index 0000000000000000000000000000000000000000..e51c777db47f414eb433760536fa95b590cf3960 GIT binary patch literal 4063 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 literal 0 HcmV?d00001 diff --git a/tests/ast-parsing/compile/using-for-4-0.8.0.sol-0.8.15-compact.zip b/tests/ast-parsing/compile/using-for-4-0.8.0.sol-0.8.15-compact.zip new file mode 100644 index 0000000000000000000000000000000000000000..12452d862d63429261cc2fb14c95004cf6299003 GIT binary patch literal 4106 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 p(a_~y{CA%JEA;af5y7DIwsbC4ru?@@ZZh1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n" + }, + "C": { + "libCall(uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/using-for-4-0.8.0.sol-0.8.15-compact.json b/tests/ast-parsing/expected/using-for-4-0.8.0.sol-0.8.15-compact.json new file mode 100644 index 000000000..6ff9c9780 --- /dev/null +++ b/tests/ast-parsing/expected/using-for-4-0.8.0.sol-0.8.15-compact.json @@ -0,0 +1,8 @@ +{ + "Lib": { + "f(St,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n" + }, + "C": { + "libCall(uint16)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/ast-parsing/using-for-3-0.8.0.sol b/tests/ast-parsing/using-for-3-0.8.0.sol new file mode 100644 index 000000000..1da4f3dc6 --- /dev/null +++ b/tests/ast-parsing/using-for-3-0.8.0.sol @@ -0,0 +1,27 @@ +using {a} for Data; + +struct Data { mapping(uint => bool) flags; } + +function a(Data storage self, uint value, uint value2) returns(bool){ + return false; +} + +library Lib { + function a(Data storage self, uint value) public + view + returns (bool) + { + return true; + } + +} + +contract C { + using Lib for Data; + Data knownValues; + + function libCall(uint value) public { + require(knownValues.a(value)); + } + +} \ No newline at end of file diff --git a/tests/ast-parsing/using-for-4-0.8.0.sol b/tests/ast-parsing/using-for-4-0.8.0.sol new file mode 100644 index 000000000..d50e107a4 --- /dev/null +++ b/tests/ast-parsing/using-for-4-0.8.0.sol @@ -0,0 +1,25 @@ +using {f} for St; +struct St { uint field; } + + +function f(St storage self, uint8 v) view returns(uint){ + return 0; +} + + +library Lib { + function f(St storage self, uint256 v) public view returns (uint) { + return 1; + } + +} + +contract C { + using Lib for St; + St st; + + function libCall(uint16 v) public view returns(uint){ + return st.f(v); // return 1 + } + +} \ No newline at end of file diff --git a/tests/test_ast_parsing.py b/tests/test_ast_parsing.py index 9d5662b91..92fd93a17 100644 --- a/tests/test_ast_parsing.py +++ b/tests/test_ast_parsing.py @@ -426,6 +426,8 @@ ALL_TESTS = [ Test("ternary-with-max.sol", ["0.8.15"]), Test("using-for-1-0.8.0.sol", ["0.8.15"]), Test("using-for-2-0.8.0.sol", ["0.8.15"]), + Test("using-for-3-0.8.0.sol", ["0.8.15"]), + Test("using-for-4-0.8.0.sol", ["0.8.15"]), Test("using-for-functions-list-1-0.8.0.sol", ["0.8.15"]), Test("using-for-functions-list-2-0.8.0.sol", ["0.8.15"]), Test("using-for-functions-list-3-0.8.0.sol", ["0.8.15"]), diff --git a/tests/test_features.py b/tests/test_features.py index c06ee96ce..1bf7ba4ff 100644 --- a/tests/test_features.py +++ b/tests/test_features.py @@ -7,6 +7,7 @@ from solc_select import solc_select from slither import Slither from slither.detectors import all_detectors from slither.detectors.abstract_detector import AbstractDetector +from slither.slithir.operations import LibraryCall def _run_all_detectors(slither: Slither): @@ -50,3 +51,23 @@ def test_funcion_id_rec_structure(): for compilation_unit in slither.compilation_units: for function in compilation_unit.functions: assert function.solidity_signature + + +def test_using_for_top_level_same_name() -> None: + slither = Slither("./tests/ast-parsing/using-for-3-0.8.0.sol") + contract_c = slither.get_contract_from_name("C")[0] + libCall = contract_c.get_function_from_full_name("libCall(uint256)") + for ir in libCall.all_slithir_operations(): + if isinstance(ir, LibraryCall) and ir.destination == "Lib" and ir.function_name == "a": + return + assert False + + +def test_using_for_top_level_implicit_conversion() -> None: + slither = Slither("./tests/ast-parsing/using-for-4-0.8.0.sol") + contract_c = slither.get_contract_from_name("C")[0] + libCall = contract_c.get_function_from_full_name("libCall(uint16)") + for ir in libCall.all_slithir_operations(): + if isinstance(ir, LibraryCall) and ir.destination == "Lib" and ir.function_name == "f": + return + assert False From ea681f9399e6060232844305c5bb9337e42fa0cb Mon Sep 17 00:00:00 2001 From: Simone Date: Thu, 5 Jan 2023 16:57:19 +0100 Subject: [PATCH 25/25] Fix testing feature --- tests/test_features.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_features.py b/tests/test_features.py index 48d65dde8..e6e781881 100644 --- a/tests/test_features.py +++ b/tests/test_features.py @@ -73,6 +73,7 @@ def test_upgradeable_comments() -> None: def test_using_for_top_level_same_name() -> None: + solc_select.switch_global_version("0.8.15", always_install=True) slither = Slither("./tests/ast-parsing/using-for-3-0.8.0.sol") contract_c = slither.get_contract_from_name("C")[0] libCall = contract_c.get_function_from_full_name("libCall(uint256)") @@ -83,6 +84,7 @@ def test_using_for_top_level_same_name() -> None: def test_using_for_top_level_implicit_conversion() -> None: + solc_select.switch_global_version("0.8.15", always_install=True) slither = Slither("./tests/ast-parsing/using-for-4-0.8.0.sol") contract_c = slither.get_contract_from_name("C")[0] libCall = contract_c.get_function_from_full_name("libCall(uint16)")