From 0e2f085d82259e4761242dd298cb755e08fa80ea Mon Sep 17 00:00:00 2001 From: Simone Date: Thu, 23 Feb 2023 13:25:03 +0100 Subject: [PATCH 1/7] Support user defined operators --- slither/core/solidity_types/type_alias.py | 5 +- .../declarations/using_for_top_level.py | 43 +++++++++----- .../expressions/expression_parsing.py | 58 +++++++++++++++---- 3 files changed, 79 insertions(+), 27 deletions(-) diff --git a/slither/core/solidity_types/type_alias.py b/slither/core/solidity_types/type_alias.py index 5b9ea0a37..a9010c7d9 100644 --- a/slither/core/solidity_types/type_alias.py +++ b/slither/core/solidity_types/type_alias.py @@ -1,10 +1,11 @@ -from typing import TYPE_CHECKING, Tuple +from typing import TYPE_CHECKING, Tuple, Dict from slither.core.children.child_contract import ChildContract from slither.core.declarations.top_level import TopLevel from slither.core.solidity_types import Type, ElementaryType if TYPE_CHECKING: + from slither.core.declarations.function_top_level import FunctionTopLevel from slither.core.declarations import Contract from slither.core.scope.scope import FileScope @@ -43,6 +44,8 @@ class TypeAliasTopLevel(TypeAlias, TopLevel): def __init__(self, underlying_type: Type, name: str, scope: "FileScope") -> None: super().__init__(underlying_type, name) self.file_scope: "FileScope" = scope + # operators redefined + self.operators: Dict[str, "FunctionTopLevel"] = {} def __str__(self) -> str: return self.name diff --git a/slither/solc_parsing/declarations/using_for_top_level.py b/slither/solc_parsing/declarations/using_for_top_level.py index b16fadc40..e20fb3bcf 100644 --- a/slither/solc_parsing/declarations/using_for_top_level.py +++ b/slither/solc_parsing/declarations/using_for_top_level.py @@ -55,22 +55,29 @@ class UsingForTopLevelSolc(CallerContextExpression): # pylint: disable=too-few- self._propagate_global(type_name) else: for f in self._functions: - full_name_split = f["function"]["name"].split(".") - if len(full_name_split) == 1: + # User defined operator + if "operator" in f: # Top level function - function_name: str = full_name_split[0] - self._analyze_top_level_function(function_name, type_name) - elif len(full_name_split) == 2: - # It can be a top level function behind an aliased import - # or a library function - first_part = full_name_split[0] - function_name = full_name_split[1] - self._check_aliased_import(first_part, function_name, type_name) + function_name: str = f["definition"]["name"] + operator: str = f["operator"] + self._analyze_operator(operator, function_name, type_name) else: - # MyImport.MyLib.a we don't care of the alias - library_name_str = full_name_split[1] - function_name = full_name_split[2] - self._analyze_library_function(library_name_str, function_name, type_name) + full_name_split = f["function"]["name"].split(".") + if len(full_name_split) == 1: + # Top level function + function_name: str = full_name_split[0] + self._analyze_top_level_function(function_name, type_name) + elif len(full_name_split) == 2: + # It can be a top level function behind an aliased import + # or a library function + first_part = full_name_split[0] + function_name = full_name_split[1] + self._check_aliased_import(first_part, function_name, type_name) + else: + # MyImport.MyLib.a we don't care of the alias + library_name_str = full_name_split[1] + function_name = full_name_split[2] + self._analyze_library_function(library_name_str, function_name, type_name) def _check_aliased_import( self, @@ -96,6 +103,14 @@ class UsingForTopLevelSolc(CallerContextExpression): # pylint: disable=too-few- self._propagate_global(type_name) break + def _analyze_operator( + self, operator: str, function_name: str, type_name: TypeAliasTopLevel + ) -> None: + for tl_function in self.compilation_unit.functions_top_level: + if tl_function.name == function_name: + type_name.operators[operator] = tl_function + break + def _analyze_library_function( self, library_name: str, diff --git a/slither/solc_parsing/expressions/expression_parsing.py b/slither/solc_parsing/expressions/expression_parsing.py index ea433a921..0727d2a16 100644 --- a/slither/solc_parsing/expressions/expression_parsing.py +++ b/slither/solc_parsing/expressions/expression_parsing.py @@ -1,6 +1,6 @@ import logging import re -from typing import Union, Dict, TYPE_CHECKING +from typing import Union, Dict, TYPE_CHECKING, List, Any import slither.core.expressions.type_conversion from slither.core.declarations.solidity_variables import ( @@ -236,6 +236,24 @@ if TYPE_CHECKING: pass +def _user_defined_op_call( + caller_context: CallerContextExpression, src, function_id: int, args: List[Any], type_call: str +) -> CallExpression: + var, was_created = find_variable(None, caller_context, function_id) + + if was_created: + var.set_offset(src, caller_context.compilation_unit) + + identifier = Identifier(var) + identifier.set_offset(src, caller_context.compilation_unit) + + var.references.append(identifier.source_mapping) + + call = CallExpression(identifier, args, type_call) + call.set_offset(src, caller_context.compilation_unit) + return call + + def parse_expression(expression: Dict, caller_context: CallerContextExpression) -> "Expression": # pylint: disable=too-many-nested-blocks,too-many-statements """ @@ -274,16 +292,24 @@ def parse_expression(expression: Dict, caller_context: CallerContextExpression) if name == "UnaryOperation": if is_compact_ast: attributes = expression - else: - attributes = expression["attributes"] - assert "prefix" in attributes - operation_type = UnaryOperationType.get_type(attributes["operator"], attributes["prefix"]) - - if is_compact_ast: expression = parse_expression(expression["subExpression"], caller_context) else: + attributes = expression["attributes"] assert len(expression["children"]) == 1 expression = parse_expression(expression["children"][0], caller_context) + assert "prefix" in attributes + + # Use of user defined operation + if "function" in attributes: + return _user_defined_op_call( + caller_context, + src, + attributes["function"], + [expression], + attributes["typeDescriptions"]["typeString"], + ) + + operation_type = UnaryOperationType.get_type(attributes["operator"], attributes["prefix"]) unary_op = UnaryOperation(expression, operation_type) unary_op.set_offset(src, caller_context.compilation_unit) return unary_op @@ -291,17 +317,25 @@ def parse_expression(expression: Dict, caller_context: CallerContextExpression) if name == "BinaryOperation": if is_compact_ast: attributes = expression - else: - attributes = expression["attributes"] - operation_type = BinaryOperationType.get_type(attributes["operator"]) - - if is_compact_ast: left_expression = parse_expression(expression["leftExpression"], caller_context) right_expression = parse_expression(expression["rightExpression"], caller_context) else: assert len(expression["children"]) == 2 + attributes = expression["attributes"] left_expression = parse_expression(expression["children"][0], caller_context) right_expression = parse_expression(expression["children"][1], caller_context) + + # Use of user defined operation + if "function" in attributes: + return _user_defined_op_call( + caller_context, + src, + attributes["function"], + [left_expression, right_expression], + attributes["typeDescriptions"]["typeString"], + ) + + operation_type = BinaryOperationType.get_type(attributes["operator"]) binary_op = BinaryOperation(left_expression, right_expression, operation_type) binary_op.set_offset(src, caller_context.compilation_unit) return binary_op From 5edc3c280e9683e42d8beff5cbe23e5f32529015 Mon Sep 17 00:00:00 2001 From: Simone Date: Fri, 10 Mar 2023 21:41:16 +0100 Subject: [PATCH 2/7] Add tests --- ...ed_operators-0.8.19.sol-0.8.19-compact.zip | Bin 0 -> 5844 bytes ...d_operators-0.8.19.sol-0.8.19-compact.json | 13 +++++ .../user_defined_operators-0.8.19.sol | 48 +++++++++++++++++ tests/test_ast_parsing.py | 1 + tests/test_features.py | 51 ++++++++++++++++++ 5 files changed, 113 insertions(+) create mode 100644 tests/ast-parsing/compile/user_defined_operators-0.8.19.sol-0.8.19-compact.zip create mode 100644 tests/ast-parsing/expected/user_defined_operators-0.8.19.sol-0.8.19-compact.json create mode 100644 tests/ast-parsing/user_defined_operators-0.8.19.sol diff --git a/tests/ast-parsing/compile/user_defined_operators-0.8.19.sol-0.8.19-compact.zip b/tests/ast-parsing/compile/user_defined_operators-0.8.19.sol-0.8.19-compact.zip new file mode 100644 index 0000000000000000000000000000000000000000..7159a1486165e875e77b28cb8132456dbea5a367 GIT binary patch literal 5844 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\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: RETURN 2\n\"];\n}\n", + "add_op(Int,Int)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n", + "lib_call(Int)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n", + "neg_usertype(Int)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: RETURN 2\n\"];\n}\n", + "neg_int(int256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n", + "eq_op(Int,Int)": "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/user_defined_operators-0.8.19.sol b/tests/ast-parsing/user_defined_operators-0.8.19.sol new file mode 100644 index 000000000..e4df845fb --- /dev/null +++ b/tests/ast-parsing/user_defined_operators-0.8.19.sol @@ -0,0 +1,48 @@ +pragma solidity ^0.8.19; + +type Int is int; +using {add as +, eq as ==, add, neg as -, Lib.f} for Int global; + +function add(Int a, Int b) pure returns (Int) { + return Int.wrap(Int.unwrap(a) + Int.unwrap(b)); +} + +function eq(Int a, Int b) pure returns (bool) { + return true; +} + +function neg(Int a) pure returns (Int) { + return a; +} + +library Lib { + function f(Int r) internal {} +} + +contract T { + function add_function_call(Int b, Int c) public returns(Int) { + Int res = add(b,c); + return res; + } + + function add_op(Int b, Int c) public returns(Int) { + return b + c; + } + + function lib_call(Int b) public { + return b.f(); + } + + function neg_usertype(Int b) public returns(Int) { + Int res = -b; + return res; + } + + function neg_int(int b) public returns(int) { + return -b; + } + + function eq_op(Int b, Int c) public returns(bool) { + return b == c; + } +} diff --git a/tests/test_ast_parsing.py b/tests/test_ast_parsing.py index c783f827f..945fde505 100644 --- a/tests/test_ast_parsing.py +++ b/tests/test_ast_parsing.py @@ -444,6 +444,7 @@ ALL_TESTS = [ Test("yul-top-level-0.8.0.sol", ["0.8.0"]), Test("complex_imports/import_aliases_issue_1319/test.sol", ["0.5.12"]), Test("yul-state-constant-access.sol", ["0.8.16"]), + Test("user_defined_operators-0.8.19.sol", ["0.8.19"]), ] # create the output folder if needed try: diff --git a/tests/test_features.py b/tests/test_features.py index d29a5eb6a..db0314b3f 100644 --- a/tests/test_features.py +++ b/tests/test_features.py @@ -160,3 +160,54 @@ def test_arithmetic_usage() -> None: assert { f.source_mapping.content_hash for f in unchecked_arithemtic_usage(slither.contracts[0]) } == {"2b4bc73cf59d486dd9043e840b5028b679354dd9", "e4ecd4d0fda7e762d29aceb8425f2c5d4d0bf962"} + + +def test_user_defined_operators() -> None: + solc_select.switch_global_version("0.8.19", always_install=True) + slither = Slither("./tests/ast-parsing/user_defined_operators-0.8.19.sol") + contract_t = slither.get_contract_from_name("T")[0] + add_function_call = contract_t.get_function_from_full_name("add_function_call(Int,Int)") + ok = False + for ir in add_function_call.all_slithir_operations(): + if isinstance(ir, InternalCall) and ir.function_name == "add": + ok = True + if not ok: + assert False + + add_op = contract_t.get_function_from_full_name("add_op(Int,Int)") + ok = False + for ir in add_op.all_slithir_operations(): + if isinstance(ir, InternalCall) and ir.function_name == "add": + ok = True + if not ok: + assert False + + lib_call = contract_t.get_function_from_full_name("lib_call(Int)") + ok = False + for ir in lib_call.all_slithir_operations(): + if isinstance(ir, LibraryCall) and ir.destination == "Lib" and ir.function_name == "f": + ok = True + if not ok: + assert False + + neg_usertype = contract_t.get_function_from_full_name("neg_usertype(Int)") + ok = False + for ir in neg_usertype.all_slithir_operations(): + if isinstance(ir, InternalCall) and ir.function_name == "neg": + ok = True + if not ok: + assert False + + neg_int = contract_t.get_function_from_full_name("neg_int(int256)") + ok = True + for ir in neg_int.all_slithir_operations(): + if isinstance(ir, InternalCall): + ok = False + if not ok: + assert False + + eq_op = contract_t.get_function_from_full_name("eq_op(Int,Int)") + for ir in eq_op.all_slithir_operations(): + if isinstance(ir, InternalCall) and ir.function_name == "eq": + return + assert False From 64abf06e0a8ef3e74fcdcc7c0956921dbc2cb4c4 Mon Sep 17 00:00:00 2001 From: Simone Date: Fri, 10 Mar 2023 21:53:32 +0100 Subject: [PATCH 3/7] Run pylint --- tests/test_features.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_features.py b/tests/test_features.py index 297023b4c..c6b670d35 100644 --- a/tests/test_features.py +++ b/tests/test_features.py @@ -203,7 +203,7 @@ def test_using_for_global_collision() -> None: sl = Slither(compilation) _run_all_detectors(sl) - +# pylint: disable=too-many-branches def test_user_defined_operators() -> None: solc_select.switch_global_version("0.8.19", always_install=True) slither = Slither("./tests/ast-parsing/user_defined_operators-0.8.19.sol") From cc650f5683490342e38b92d6e681d44b07e706e5 Mon Sep 17 00:00:00 2001 From: Simone Date: Fri, 10 Mar 2023 22:04:50 +0100 Subject: [PATCH 4/7] Black --- tests/test_features.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_features.py b/tests/test_features.py index c6b670d35..748f1df9e 100644 --- a/tests/test_features.py +++ b/tests/test_features.py @@ -203,6 +203,7 @@ def test_using_for_global_collision() -> None: sl = Slither(compilation) _run_all_detectors(sl) + # pylint: disable=too-many-branches def test_user_defined_operators() -> None: solc_select.switch_global_version("0.8.19", always_install=True) From a6fb92f17962cb8811782921163561b94cb7a6bd Mon Sep 17 00:00:00 2001 From: Simone Date: Fri, 23 Jun 2023 09:19:31 +0200 Subject: [PATCH 5/7] Bound function search to first parameter type --- slither/solc_parsing/declarations/using_for_top_level.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/slither/solc_parsing/declarations/using_for_top_level.py b/slither/solc_parsing/declarations/using_for_top_level.py index 8ac5b4462..3b7bb280c 100644 --- a/slither/solc_parsing/declarations/using_for_top_level.py +++ b/slither/solc_parsing/declarations/using_for_top_level.py @@ -112,7 +112,12 @@ class UsingForTopLevelSolc(CallerContextExpression): # pylint: disable=too-few- self, operator: str, function_name: str, type_name: TypeAliasTopLevel ) -> None: for tl_function in self.compilation_unit.functions_top_level: - if tl_function.name == function_name: + # The library function is bound to the first parameter's type + if ( + tl_function.name == function_name + and tl_function.parameters + and type_name == tl_function.parameters[0].type + ): type_name.operators[operator] = tl_function break From 3f1db7af7799da2172b9b8e93b839131602ec91c Mon Sep 17 00:00:00 2001 From: Simone Date: Fri, 23 Jun 2023 09:21:03 +0200 Subject: [PATCH 6/7] Look for library functions in the current scope --- slither/solc_parsing/declarations/using_for_top_level.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slither/solc_parsing/declarations/using_for_top_level.py b/slither/solc_parsing/declarations/using_for_top_level.py index 3b7bb280c..fe72e5780 100644 --- a/slither/solc_parsing/declarations/using_for_top_level.py +++ b/slither/solc_parsing/declarations/using_for_top_level.py @@ -111,7 +111,7 @@ class UsingForTopLevelSolc(CallerContextExpression): # pylint: disable=too-few- def _analyze_operator( self, operator: str, function_name: str, type_name: TypeAliasTopLevel ) -> None: - for tl_function in self.compilation_unit.functions_top_level: + for tl_function in self._using_for.file_scope.functions: # The library function is bound to the first parameter's type if ( tl_function.name == function_name From f2accfd77d3d256be0e300c376abdbb7fc2443b9 Mon Sep 17 00:00:00 2001 From: Simone Date: Fri, 23 Jun 2023 09:32:26 +0200 Subject: [PATCH 7/7] Add test --- tests/e2e/solc_parsing/test_ast_parsing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/e2e/solc_parsing/test_ast_parsing.py b/tests/e2e/solc_parsing/test_ast_parsing.py index b694d1044..307e6736f 100644 --- a/tests/e2e/solc_parsing/test_ast_parsing.py +++ b/tests/e2e/solc_parsing/test_ast_parsing.py @@ -458,6 +458,7 @@ ALL_TESTS = [ "assembly-functions.sol", ["0.6.9", "0.7.6", "0.8.16"], ), + Test("user_defined_operators-0.8.19.sol", ["0.8.19"]), ] # create the output folder if needed try: