From 0ee1c64959e339387b25dfb83dc3fce6bff7e548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Tue, 8 Nov 2022 17:14:01 -0300 Subject: [PATCH 01/23] fail-on: rework feature This adds a new `fail_on` config option that can be changed with a mutually exclusive group of argument flags. It also decouples the exclude_* and fail_on flags, so you can do things like `fail_on: pedantic` while disabling optimization findings. Additionally, this adds some new code to detect the old-style config options, migrate their settings, and alert the user. Fixes #1458 --- slither/__main__.py | 78 +++++++++++++++++++---------------- slither/utils/command_line.py | 50 ++++++++++++++++++++-- 2 files changed, 88 insertions(+), 40 deletions(-) diff --git a/slither/__main__.py b/slither/__main__.py index 70357586e9..a2402fb606 100644 --- a/slither/__main__.py +++ b/slither/__main__.py @@ -28,6 +28,8 @@ from slither.utils.output_capture import StandardOutputCapture from slither.utils.colors import red, set_colorization_enabled from slither.utils.command_line import ( + FailOnLevel, + migrate_config_options, output_detectors, output_results_to_markdown, output_detectors_json, @@ -220,22 +222,22 @@ def choose_detectors( detectors_to_run = sorted(detectors_to_run, key=lambda x: x.IMPACT) return detectors_to_run - if args.exclude_optimization and not args.fail_pedantic: + if args.exclude_optimization: detectors_to_run = [ d for d in detectors_to_run if d.IMPACT != DetectorClassification.OPTIMIZATION ] - if args.exclude_informational and not args.fail_pedantic: + if args.exclude_informational: detectors_to_run = [ d for d in detectors_to_run if d.IMPACT != DetectorClassification.INFORMATIONAL ] - if args.exclude_low and not args.fail_low: + if args.exclude_low: detectors_to_run = [d for d in detectors_to_run if d.IMPACT != DetectorClassification.LOW] - if args.exclude_medium and not args.fail_medium: + if args.exclude_medium: detectors_to_run = [ d for d in detectors_to_run if d.IMPACT != DetectorClassification.MEDIUM ] - if args.exclude_high and not args.fail_high: + if args.exclude_high: detectors_to_run = [d for d in detectors_to_run if d.IMPACT != DetectorClassification.HIGH] if args.detectors_to_exclude: detectors_to_run = [ @@ -401,41 +403,44 @@ def parse_args( default=defaults_flag_in_config["exclude_high"], ) - group_detector.add_argument( + fail_on_group = group_detector.add_mutually_exclusive_group() + fail_on_group.add_argument( "--fail-pedantic", - help="Return the number of findings in the exit code", - action="store_true", - default=defaults_flag_in_config["fail_pedantic"], + help="Fail if any findings are detected", + action="store_const", + dest="fail_on", + const=FailOnLevel.PEDANTIC, ) - - group_detector.add_argument( - "--no-fail-pedantic", - help="Do not return the number of findings in the exit code. Opposite of --fail-pedantic", - dest="fail_pedantic", - action="store_false", - required=False, - ) - - group_detector.add_argument( + fail_on_group.add_argument( "--fail-low", - help="Fail if low or greater impact finding is detected", - action="store_true", - default=defaults_flag_in_config["fail_low"], + help="Fail if any low or greater impact findings are detected", + action="store_const", + dest="fail_on", + const=FailOnLevel.LOW, ) - - group_detector.add_argument( + fail_on_group.add_argument( "--fail-medium", - help="Fail if medium or greater impact finding is detected", - action="store_true", - default=defaults_flag_in_config["fail_medium"], + help="Fail if any medium or greater impact findings are detected", + action="store_const", + dest="fail_on", + const=FailOnLevel.MEDIUM, ) - - group_detector.add_argument( + fail_on_group.add_argument( "--fail-high", - help="Fail if high impact finding is detected", - action="store_true", - default=defaults_flag_in_config["fail_high"], + help="Fail if any high impact findings are detected", + action="store_const", + dest="fail_on", + const=FailOnLevel.HIGH, + ) + fail_on_group.add_argument( + "--fail-none", + "--no-fail-pedantic", + help="Do not return the number of findings in the exit code", + action="store_const", + dest="fail_on", + const=FailOnLevel.NONE, ) + fail_on_group.set_defaults(fail_on=FailOnLevel.PEDANTIC) group_detector.add_argument( "--show-ignored-findings", @@ -910,17 +915,18 @@ def main_impl( stats = pstats.Stats(cp).sort_stats("cumtime") stats.print_stats() - if args.fail_high: + fail_on = FailOnLevel(args.fail_on) + if fail_on == FailOnLevel.HIGH: fail_on_detection = any(result["impact"] == "High" for result in results_detectors) - elif args.fail_medium: + elif fail_on == FailOnLevel.MEDIUM: fail_on_detection = any( result["impact"] in ["Medium", "High"] for result in results_detectors ) - elif args.fail_low: + elif fail_on == FailOnLevel.LOW: fail_on_detection = any( result["impact"] in ["Low", "Medium", "High"] for result in results_detectors ) - elif args.fail_pedantic: + elif fail_on == FailOnLevel.PEDANTIC: fail_on_detection = bool(results_detectors) else: fail_on_detection = False diff --git a/slither/utils/command_line.py b/slither/utils/command_line.py index c2fef5eca0..a650960f56 100644 --- a/slither/utils/command_line.py +++ b/slither/utils/command_line.py @@ -1,4 +1,5 @@ import argparse +import enum import json import os import re @@ -27,6 +28,15 @@ "list-printers", ] + +class FailOnLevel(enum.Enum): + PEDANTIC = "pedantic" + LOW = "low" + MEDIUM = "medium" + HIGH = "high" + NONE = "none" + + # Those are the flags shared by the command line and the config file defaults_flag_in_config = { "detectors_to_run": "all", @@ -38,10 +48,7 @@ "exclude_low": False, "exclude_medium": False, "exclude_high": False, - "fail_pedantic": True, - "fail_low": False, - "fail_medium": False, - "fail_high": False, + "fail_on": FailOnLevel.PEDANTIC.value, "json": None, "sarif": None, "json-types": ",".join(DEFAULT_JSON_OUTPUT_TYPES), @@ -57,6 +64,13 @@ **DEFAULTS_FLAG_IN_CONFIG_CRYTIC_COMPILE, } +deprecated_flags = { + "fail_pedantic": True, + "fail_low": False, + "fail_medium": False, + "fail_high": False, +} + def read_config_file(args: argparse.Namespace) -> None: # No config file was provided as an argument @@ -73,6 +87,12 @@ def read_config_file(args: argparse.Namespace) -> None: with open(args.config_file, encoding="utf8") as f: config = json.load(f) for key, elem in config.items(): + if key in deprecated_flags: + logger.info( + yellow(f"{args.config_file} has a deprecated key: {key} : {elem}") + ) + migrate_config_options(args, key, elem) + continue if key not in defaults_flag_in_config: logger.info( yellow(f"{args.config_file} has an unknown key: {key} : {elem}") @@ -87,6 +107,28 @@ def read_config_file(args: argparse.Namespace) -> None: logger.error(yellow("Falling back to the default settings...")) +def migrate_config_options(args: argparse.Namespace, key: str, elem): + if key.startswith("fail_") and getattr(args, "fail_on") == defaults_flag_in_config["fail_on"]: + if key == "fail_pedantic": + pedantic_setting = elem + fail_on = pedantic_setting and FailOnLevel.PEDANTIC or FailOnLevel.NONE + setattr(args, "fail_on", fail_on) + logger.info( + "Migrating fail_pedantic: {} as fail_on: {}".format(pedantic_setting, fail_on.value) + ) + elif key == "fail_low" and elem == True: + logger.info("Migrating fail_low: true -> fail_on: low") + setattr(args, "fail_on", FailOnLevel.LOW) + elif key == "fail_medium" and elem == True: + logger.info("Migrating fail_medium: true -> fail_on: medium") + setattr(args, "fail_on", FailOnLevel.MEDIUM) + elif key == "fail_high" and elem == True: + logger.info("Migrating fail_high: true -> fail_on: high") + setattr(args, "fail_on", FailOnLevel.HIGH) + else: + logger.warn(yellow("Key {} was deprecated but no migration was provided".format(key))) + + def output_to_markdown( detector_classes: List[Type[AbstractDetector]], printer_classes: List[Type[AbstractPrinter]], From 0e2f085d82259e4761242dd298cb755e08fa80ea Mon Sep 17 00:00:00 2001 From: Simone Date: Thu, 23 Feb 2023 13:25:03 +0100 Subject: [PATCH 02/23] 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 5b9ea0a37f..a9010c7d9d 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 b16fadc402..e20fb3bcfc 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 @@ def analyze(self) -> None: 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 @@ def _analyze_top_level_function( 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 ea433a9216..0727d2a16a 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 @@ def _parse_elementary_type_name_expression( 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 03/23] 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 0000000000..e4df845fb3 --- /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 c783f827f9..945fde5055 100644 --- a/tests/test_ast_parsing.py +++ b/tests/test_ast_parsing.py @@ -444,6 +444,7 @@ def make_version(minor: int, patch_min: int, patch_max: int) -> List[str]: 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 d29a5eb6af..db0314b3f5 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 04/23] 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 297023b4c4..c6b670d353 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 05/23] Black --- tests/test_features.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_features.py b/tests/test_features.py index c6b670d353..748f1df9e0 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 f48189951b95f752540043aa3c427f2a12f6aaaa Mon Sep 17 00:00:00 2001 From: bart1e Date: Sun, 26 Feb 2023 17:39:01 +0100 Subject: [PATCH 06/23] Detector for array length caching added --- slither/detectors/all_detectors.py | 1 + .../operations/cache_array_length.py | 215 ++++++++++++++++++ .../0.8.17/CacheArrayLength.sol | 127 +++++++++++ ...rayLength.sol.0.8.17.CacheArrayLength.json | 74 ++++++ tests/e2e/detectors/test_detectors.py | 5 + 5 files changed, 422 insertions(+) create mode 100644 slither/detectors/operations/cache_array_length.py create mode 100644 tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol create mode 100644 tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol.0.8.17.CacheArrayLength.json diff --git a/slither/detectors/all_detectors.py b/slither/detectors/all_detectors.py index 9722b87937..6a1ec67390 100644 --- a/slither/detectors/all_detectors.py +++ b/slither/detectors/all_detectors.py @@ -89,3 +89,4 @@ from .functions.permit_domain_signature_collision import DomainSeparatorCollision from .functions.codex import Codex from .functions.cyclomatic_complexity import CyclomaticComplexity +from .operations.cache_array_length import CacheArrayLength diff --git a/slither/detectors/operations/cache_array_length.py b/slither/detectors/operations/cache_array_length.py new file mode 100644 index 0000000000..8c1112d2d9 --- /dev/null +++ b/slither/detectors/operations/cache_array_length.py @@ -0,0 +1,215 @@ +from typing import List, Set + +from slither.core.cfg.node import Node, NodeType +from slither.core.declarations import Function +from slither.core.expressions import BinaryOperation, Identifier, MemberAccess, UnaryOperation +from slither.core.solidity_types import ArrayType +from slither.core.source_mapping.source_mapping import SourceMapping +from slither.core.variables import StateVariable +from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification +from slither.slithir.operations import Length, Delete + + +class CacheArrayLength(AbstractDetector): + """ + Detects `for` loops that use `length` member of some storage array in their loop condition and don't modify it. + """ + + ARGUMENT = "cache-array-length" + HELP = ( + "Detects `for` loops that use `length` member of some storage array in their loop condition and don't " + "modify it. " + ) + IMPACT = DetectorClassification.OPTIMIZATION + CONFIDENCE = DetectorClassification.HIGH + + WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#cache-array-length" + + WIKI_TITLE = "Cache array length" + WIKI_DESCRIPTION = ( + "Detects `for` loops that use `length` member of some storage array in their loop condition " + "and don't modify it. " + ) + WIKI_EXPLOIT_SCENARIO = """ +```solidity +contract C +{ + uint[] array; + + function f() public + { + for (uint i = 0; i < array.length; i++) + { + // code that does not modify length of `array` + } + } +} +``` +Since the `for` loop in `f` doesn't modify `array.length`, it is more gas efficient to cache it in some local variable and use that variable instead, like in the following example: + +```solidity +contract C +{ + uint[] array; + + function f() public + { + uint array_length = array.length; + for (uint i = 0; i < array_length; i++) + { + // code that does not modify length of `array` + } + } +} +``` + """ + WIKI_RECOMMENDATION = ( + "Cache the lengths of storage arrays if they are used and not modified in `for` loops." + ) + + @staticmethod + def _is_identifier_member_access_comparison(exp: BinaryOperation) -> bool: + """ + Checks whether a BinaryOperation `exp` is an operation on Identifier and MemberAccess. + """ + return ( + isinstance(exp.expression_left, Identifier) + and isinstance(exp.expression_right, MemberAccess) + ) or ( + isinstance(exp.expression_left, MemberAccess) + and isinstance(exp.expression_right, Identifier) + ) + + @staticmethod + def _extract_array_from_length_member_access(exp: MemberAccess) -> [StateVariable | None]: + """ + Given a member access `exp`, it returns state array which `length` member is accessed through `exp`. + If array is not a state array or its `length` member is not referenced, it returns `None`. + """ + if exp.member_name != "length": + return None + if not isinstance(exp.expression, Identifier): + return None + if not isinstance(exp.expression.value, StateVariable): + return None + if not isinstance(exp.expression.value.type, ArrayType): + return None + return exp.expression.value + + @staticmethod + def _is_loop_referencing_array_length( + node: Node, visited: Set[Node], array: StateVariable, depth: int + ) -> True: + """ + For a given loop, checks if it references `array.length` at some point. + Will also return True if `array.length` is referenced but not changed. + This may potentially generate false negatives in the detector, but it was done this way because: + - situations when array `length` is referenced but not modified in loop are rare + - checking if `array.length` is indeed modified would require much more work + """ + visited.add(node) + if node.type == NodeType.STARTLOOP: + depth += 1 + if node.type == NodeType.ENDLOOP: + depth -= 1 + if depth == 0: + return False + + # Array length may change in the following situations: + # - when `push` is called + # - when `pop` is called + # - when `delete` is called on the entire array + if node.type == NodeType.EXPRESSION: + for op in node.irs: + if isinstance(op, Length) and op.value == array: + # op accesses array.length, not necessarily modifying it + return True + if isinstance(op, Delete): + # take into account only delete entire array, since delete array[i] doesn't change `array.length` + if ( + isinstance(op.expression, UnaryOperation) + and isinstance(op.expression.expression, Identifier) + and op.expression.expression.value == array + ): + return True + + for son in node.sons: + if son not in visited: + if CacheArrayLength._is_loop_referencing_array_length(son, visited, array, depth): + return True + return False + + @staticmethod + def _handle_loops(nodes: List[Node], non_optimal_array_len_usages: List[SourceMapping]) -> None: + """ + For each loop, checks if it has a comparison with `length` array member and, if it has, checks whether that + array size could potentially change in that loop. + If it cannot, the loop condition is added to `non_optimal_array_len_usages`. + There may be some false negatives here - see docs for `_is_loop_referencing_array_length` for more information. + """ + for node in nodes: + if node.type == NodeType.STARTLOOP: + if_node = node.sons[0] + if if_node.type != NodeType.IFLOOP: + continue + if not isinstance(if_node.expression, BinaryOperation): + continue + exp: BinaryOperation = if_node.expression + if not CacheArrayLength._is_identifier_member_access_comparison(exp): + continue + array: StateVariable + if isinstance(exp.expression_right, MemberAccess): + array = CacheArrayLength._extract_array_from_length_member_access( + exp.expression_right + ) + else: # isinstance(exp.expression_left, MemberAccess) == True + array = CacheArrayLength._extract_array_from_length_member_access( + exp.expression_left + ) + if array is None: + continue + + visited: Set[Node] = set() + if not CacheArrayLength._is_loop_referencing_array_length( + if_node, visited, array, 1 + ): + non_optimal_array_len_usages.append(if_node.expression) + + @staticmethod + def _get_non_optimal_array_len_usages_for_function(f: Function) -> List[SourceMapping]: + """ + Finds non-optimal usages of array length in loop conditions in a given function. + """ + non_optimal_array_len_usages: List[SourceMapping] = [] + CacheArrayLength._handle_loops(f.nodes, non_optimal_array_len_usages) + + return non_optimal_array_len_usages + + @staticmethod + def _get_non_optimal_array_len_usages(functions: List[Function]) -> List[SourceMapping]: + """ + Finds non-optimal usages of array length in loop conditions in given functions. + """ + non_optimal_array_len_usages: List[SourceMapping] = [] + + for f in functions: + non_optimal_array_len_usages += ( + CacheArrayLength._get_non_optimal_array_len_usages_for_function(f) + ) + + return non_optimal_array_len_usages + + def _detect(self): + results = [] + + non_optimal_array_len_usages = CacheArrayLength._get_non_optimal_array_len_usages( + self.compilation_unit.functions + ) + for usage in non_optimal_array_len_usages: + info = [ + f"Loop condition at {usage.source_mapping} should use cached array length instead of referencing " + f"`length` member of the storage array.\n " + ] + res = self.generate_result(info) + results.append(res) + return results diff --git a/tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol b/tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol new file mode 100644 index 0000000000..79858d1825 --- /dev/null +++ b/tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol @@ -0,0 +1,127 @@ +pragma solidity 0.8.17; + +contract CacheArrayLength +{ + struct S + { + uint s; + } + + S[] array; + S[] array2; + + function f() public + { + // array accessed but length doesn't change + for (uint i = 0; i < array.length; i++) // warning should appear + { + array[i] = S(0); + } + + // array.length doesn't change, but array.length not used in loop condition + for (uint i = array.length; i >= 0; i--) + { + + } + + // array.length changes in the inner loop + for (uint i = 0; i < array.length; i++) + { + for (uint j = i; j < 2 * i; j++) + array.push(S(j)); + } + + // array.length changes + for (uint i = 0; i < array.length; i++) + { + array.pop(); + } + + // array.length changes + for (uint i = 0; i < array.length; i++) + { + delete array; + } + + // array.length doesn't change despite using delete + for (uint i = 0; i < array.length; i++) // warning should appear + { + delete array[i]; + } + + // array.length changes; push used in more complex expression + for (uint i = 0; i < array.length; i++) + { + array.push() = S(i); + } + + // array.length doesn't change + for (uint i = 0; i < array.length; i++) // warning should appear + { + array2.pop(); + array2.push(); + array2.push(S(i)); + delete array2; + delete array[0]; + } + + // array.length changes; array2.length doesn't change + for (uint i = 0; i < 7; i++) + { + for (uint j = i; j < array.length; j++) + { + for (uint k = 0; k < j; k++) + { + + } + + for (uint k = 0; k < array2.length; k++) // warning should appear + { + array.pop(); + } + } + } + + // array.length doesn't change; array2.length changes + for (uint i = 0; i < 7; i++) + { + for (uint j = i; j < array.length; j++) // warning should appear + { + for (uint k = 0; k < j; k++) + { + + } + + for (uint k = 0; k < array2.length; k++) + { + array2.pop(); + } + } + } + + // none of array.length and array2.length changes + for (uint i = 0; i < 7; i++) + { + for (uint j = i; j < array.length; j++) // warning should appear + { + for (uint k = 0; k < j; k++) + { + + } + + for (uint k = 0; k < array2.length; k++) // warning should appear + { + + } + } + } + + S[] memory array3; + + // array3 not modified, but it's not a storage array + for (uint i = 0; i < array3.length; i++) + { + + } + } +} \ No newline at end of file diff --git a/tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol.0.8.17.CacheArrayLength.json b/tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol.0.8.17.CacheArrayLength.json new file mode 100644 index 0000000000..b2050508fb --- /dev/null +++ b/tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol.0.8.17.CacheArrayLength.json @@ -0,0 +1,74 @@ +[ + [ + { + "elements": [], + "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#88 should use cached array length instead of referencing `length` member of the storage array.\n", + "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#88 should use cached array length instead of referencing `length` member of the storage array.\n", + "first_markdown_element": "", + "id": "66f0059f2e2608b9e532ab9f6a473aa9128f1e9f02fe16fcb960b4ae4970b2cb", + "check": "cache-array-length", + "impact": "Optimization", + "confidence": "High" + }, + { + "elements": [], + "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#16 should use cached array length instead of referencing `length` member of the storage array.\n", + "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#16 should use cached array length instead of referencing `length` member of the storage array.\n", + "first_markdown_element": "", + "id": "8463e2cd88985588ad2426476df033957fb4f8969117b7649f1165244b86d2e0", + "check": "cache-array-length", + "impact": "Optimization", + "confidence": "High" + }, + { + "elements": [], + "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#105 should use cached array length instead of referencing `length` member of the storage array.\n", + "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#105 should use cached array length instead of referencing `length` member of the storage array.\n", + "first_markdown_element": "", + "id": "961db8345eea46c3814eb2c6f6fc6d1506a2feaa85630349266cff940f33a7ab", + "check": "cache-array-length", + "impact": "Optimization", + "confidence": "High" + }, + { + "elements": [], + "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#59 should use cached array length instead of referencing `length` member of the storage array.\n", + "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#59 should use cached array length instead of referencing `length` member of the storage array.\n", + "first_markdown_element": "", + "id": "aea0a11159dec6d660f39a09aa44b56ad0effb7ced7183552b21b69f0f62e0c7", + "check": "cache-array-length", + "impact": "Optimization", + "confidence": "High" + }, + { + "elements": [], + "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#78 should use cached array length instead of referencing `length` member of the storage array.\n", + "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#78 should use cached array length instead of referencing `length` member of the storage array.\n", + "first_markdown_element": "", + "id": "d296a0fd4cbe7b9a2db11b3d08b2db08a53414326a91eeda1f78853c4d4c5714", + "check": "cache-array-length", + "impact": "Optimization", + "confidence": "High" + }, + { + "elements": [], + "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#112 should use cached array length instead of referencing `length` member of the storage array.\n", + "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#112 should use cached array length instead of referencing `length` member of the storage array.\n", + "first_markdown_element": "", + "id": "db2bfdc710cfa1ea5339e1f14b1a5b80735c50c37522f983a220b579df34c5d0", + "check": "cache-array-length", + "impact": "Optimization", + "confidence": "High" + }, + { + "elements": [], + "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#47 should use cached array length instead of referencing `length` member of the storage array.\n", + "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#47 should use cached array length instead of referencing `length` member of the storage array.\n", + "first_markdown_element": "", + "id": "f5085e5d1ddd69d59f60443b7e89ea5f7301ba6a0a42786624b783389474aaf7", + "check": "cache-array-length", + "impact": "Optimization", + "confidence": "High" + } + ] +] \ No newline at end of file diff --git a/tests/e2e/detectors/test_detectors.py b/tests/e2e/detectors/test_detectors.py index e6b87d530a..5a91802562 100644 --- a/tests/e2e/detectors/test_detectors.py +++ b/tests/e2e/detectors/test_detectors.py @@ -1639,6 +1639,11 @@ def id_test(test_item: Test): "LowCyclomaticComplexity.sol", "0.8.16", ), + Test( + all_detectors.CacheArrayLength, + "CacheArrayLength.sol", + "0.8.17", + ), ] GENERIC_PATH = "/GENERIC_PATH" From 71fef2c437ca97bc7e303f6eadc615252ef78726 Mon Sep 17 00:00:00 2001 From: bart1e Date: Sun, 26 Feb 2023 18:11:34 +0100 Subject: [PATCH 07/23] 'unsupported operand type' error fix --- .../operations/cache_array_length.py | 2 +- ...rayLength.sol.0.8.17.CacheArrayLength.json | 42 +++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/slither/detectors/operations/cache_array_length.py b/slither/detectors/operations/cache_array_length.py index 8c1112d2d9..1f8111bdb4 100644 --- a/slither/detectors/operations/cache_array_length.py +++ b/slither/detectors/operations/cache_array_length.py @@ -81,7 +81,7 @@ def _is_identifier_member_access_comparison(exp: BinaryOperation) -> bool: ) @staticmethod - def _extract_array_from_length_member_access(exp: MemberAccess) -> [StateVariable | None]: + def _extract_array_from_length_member_access(exp: MemberAccess) -> StateVariable: """ Given a member access `exp`, it returns state array which `length` member is accessed through `exp`. If array is not a state array or its `length` member is not referenced, it returns `None`. diff --git a/tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol.0.8.17.CacheArrayLength.json b/tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol.0.8.17.CacheArrayLength.json index b2050508fb..3323ff4791 100644 --- a/tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol.0.8.17.CacheArrayLength.json +++ b/tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol.0.8.17.CacheArrayLength.json @@ -2,70 +2,70 @@ [ { "elements": [], - "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#88 should use cached array length instead of referencing `length` member of the storage array.\n", - "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#88 should use cached array length instead of referencing `length` member of the storage array.\n", + "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#105 should use cached array length instead of referencing `length` member of the storage array.\n ", + "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#105 should use cached array length instead of referencing `length` member of the storage array.\n ", "first_markdown_element": "", - "id": "66f0059f2e2608b9e532ab9f6a473aa9128f1e9f02fe16fcb960b4ae4970b2cb", + "id": "2ff6144814e406cadadd58712f5a7a8ef6ee169da06660d590e7bee37759fc98", "check": "cache-array-length", "impact": "Optimization", "confidence": "High" }, { "elements": [], - "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#16 should use cached array length instead of referencing `length` member of the storage array.\n", - "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#16 should use cached array length instead of referencing `length` member of the storage array.\n", + "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#59 should use cached array length instead of referencing `length` member of the storage array.\n ", + "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#59 should use cached array length instead of referencing `length` member of the storage array.\n ", "first_markdown_element": "", - "id": "8463e2cd88985588ad2426476df033957fb4f8969117b7649f1165244b86d2e0", + "id": "48a6388cf2193fdd780ea86cb3e588dfd3276182e209f3f2807d9927a5ba25bc", "check": "cache-array-length", "impact": "Optimization", "confidence": "High" }, { "elements": [], - "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#105 should use cached array length instead of referencing `length` member of the storage array.\n", - "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#105 should use cached array length instead of referencing `length` member of the storage array.\n", + "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#47 should use cached array length instead of referencing `length` member of the storage array.\n ", + "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#47 should use cached array length instead of referencing `length` member of the storage array.\n ", "first_markdown_element": "", - "id": "961db8345eea46c3814eb2c6f6fc6d1506a2feaa85630349266cff940f33a7ab", + "id": "562b7ae618977ea1d0a232d8af5edd81ce404b6844e864d0c4ab162a88142c71", "check": "cache-array-length", "impact": "Optimization", "confidence": "High" }, { "elements": [], - "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#59 should use cached array length instead of referencing `length` member of the storage array.\n", - "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#59 should use cached array length instead of referencing `length` member of the storage array.\n", + "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#112 should use cached array length instead of referencing `length` member of the storage array.\n ", + "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#112 should use cached array length instead of referencing `length` member of the storage array.\n ", "first_markdown_element": "", - "id": "aea0a11159dec6d660f39a09aa44b56ad0effb7ced7183552b21b69f0f62e0c7", + "id": "8f1aa2da0763f65179e90a2b96d7feb68c99aab60f227a5da8dad2f12069f047", "check": "cache-array-length", "impact": "Optimization", "confidence": "High" }, { "elements": [], - "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#78 should use cached array length instead of referencing `length` member of the storage array.\n", - "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#78 should use cached array length instead of referencing `length` member of the storage array.\n", + "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#88 should use cached array length instead of referencing `length` member of the storage array.\n ", + "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#88 should use cached array length instead of referencing `length` member of the storage array.\n ", "first_markdown_element": "", - "id": "d296a0fd4cbe7b9a2db11b3d08b2db08a53414326a91eeda1f78853c4d4c5714", + "id": "9c988bc3f6748fadb8527c9eea53e3d06f5f4e7b2a0c7c70993c4af758bbba47", "check": "cache-array-length", "impact": "Optimization", "confidence": "High" }, { "elements": [], - "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#112 should use cached array length instead of referencing `length` member of the storage array.\n", - "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#112 should use cached array length instead of referencing `length` member of the storage array.\n", + "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#78 should use cached array length instead of referencing `length` member of the storage array.\n ", + "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#78 should use cached array length instead of referencing `length` member of the storage array.\n ", "first_markdown_element": "", - "id": "db2bfdc710cfa1ea5339e1f14b1a5b80735c50c37522f983a220b579df34c5d0", + "id": "a1c39b4ae47535f3354d0bf26ea67d3b524efb94a3bdb2f503bda245471ee451", "check": "cache-array-length", "impact": "Optimization", "confidence": "High" }, { "elements": [], - "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#47 should use cached array length instead of referencing `length` member of the storage array.\n", - "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#47 should use cached array length instead of referencing `length` member of the storage array.\n", + "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#16 should use cached array length instead of referencing `length` member of the storage array.\n ", + "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#16 should use cached array length instead of referencing `length` member of the storage array.\n ", "first_markdown_element": "", - "id": "f5085e5d1ddd69d59f60443b7e89ea5f7301ba6a0a42786624b783389474aaf7", + "id": "c57b54ebb07e5114ccb8c124fa3971b9385d5252264f2bf2acbe131b3a986aa8", "check": "cache-array-length", "impact": "Optimization", "confidence": "High" From d1804f3d01edb309f9632ac0ba67e0ca7e8fdce3 Mon Sep 17 00:00:00 2001 From: bart1e Date: Wed, 3 May 2023 19:20:05 +0200 Subject: [PATCH 08/23] Tests updated --- ...yLength_0_8_17_CacheArrayLength_sol__0.txt | 14 ++ .../0.8.17/CacheArrayLength.sol | 127 ++++++++++++++++++ .../0.8.17/CacheArrayLength.sol-0.8.17.zip | Bin 0 -> 7589 bytes 3 files changed, 141 insertions(+) create mode 100644 tests/e2e/detectors/snapshots/detectors__detector_CacheArrayLength_0_8_17_CacheArrayLength_sol__0.txt create mode 100644 tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol create mode 100644 tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol-0.8.17.zip diff --git a/tests/e2e/detectors/snapshots/detectors__detector_CacheArrayLength_0_8_17_CacheArrayLength_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_CacheArrayLength_0_8_17_CacheArrayLength_sol__0.txt new file mode 100644 index 0000000000..a0ba357409 --- /dev/null +++ b/tests/e2e/detectors/snapshots/detectors__detector_CacheArrayLength_0_8_17_CacheArrayLength_sol__0.txt @@ -0,0 +1,14 @@ +Loop condition at tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#47 should use cached array length instead of referencing `length` member of the storage array. + +Loop condition at tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#78 should use cached array length instead of referencing `length` member of the storage array. + +Loop condition at tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#16 should use cached array length instead of referencing `length` member of the storage array. + +Loop condition at tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#88 should use cached array length instead of referencing `length` member of the storage array. + +Loop condition at tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#105 should use cached array length instead of referencing `length` member of the storage array. + +Loop condition at tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#112 should use cached array length instead of referencing `length` member of the storage array. + +Loop condition at tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#59 should use cached array length instead of referencing `length` member of the storage array. + diff --git a/tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol b/tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol new file mode 100644 index 0000000000..79858d1825 --- /dev/null +++ b/tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol @@ -0,0 +1,127 @@ +pragma solidity 0.8.17; + +contract CacheArrayLength +{ + struct S + { + uint s; + } + + S[] array; + S[] array2; + + function f() public + { + // array accessed but length doesn't change + for (uint i = 0; i < array.length; i++) // warning should appear + { + array[i] = S(0); + } + + // array.length doesn't change, but array.length not used in loop condition + for (uint i = array.length; i >= 0; i--) + { + + } + + // array.length changes in the inner loop + for (uint i = 0; i < array.length; i++) + { + for (uint j = i; j < 2 * i; j++) + array.push(S(j)); + } + + // array.length changes + for (uint i = 0; i < array.length; i++) + { + array.pop(); + } + + // array.length changes + for (uint i = 0; i < array.length; i++) + { + delete array; + } + + // array.length doesn't change despite using delete + for (uint i = 0; i < array.length; i++) // warning should appear + { + delete array[i]; + } + + // array.length changes; push used in more complex expression + for (uint i = 0; i < array.length; i++) + { + array.push() = S(i); + } + + // array.length doesn't change + for (uint i = 0; i < array.length; i++) // warning should appear + { + array2.pop(); + array2.push(); + array2.push(S(i)); + delete array2; + delete array[0]; + } + + // array.length changes; array2.length doesn't change + for (uint i = 0; i < 7; i++) + { + for (uint j = i; j < array.length; j++) + { + for (uint k = 0; k < j; k++) + { + + } + + for (uint k = 0; k < array2.length; k++) // warning should appear + { + array.pop(); + } + } + } + + // array.length doesn't change; array2.length changes + for (uint i = 0; i < 7; i++) + { + for (uint j = i; j < array.length; j++) // warning should appear + { + for (uint k = 0; k < j; k++) + { + + } + + for (uint k = 0; k < array2.length; k++) + { + array2.pop(); + } + } + } + + // none of array.length and array2.length changes + for (uint i = 0; i < 7; i++) + { + for (uint j = i; j < array.length; j++) // warning should appear + { + for (uint k = 0; k < j; k++) + { + + } + + for (uint k = 0; k < array2.length; k++) // warning should appear + { + + } + } + } + + S[] memory array3; + + // array3 not modified, but it's not a storage array + for (uint i = 0; i < array3.length; i++) + { + + } + } +} \ No newline at end of file diff --git a/tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol-0.8.17.zip b/tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol-0.8.17.zip new file mode 100644 index 0000000000000000000000000000000000000000..ab3d813718b45d438f91556934cc243e900beb57 GIT binary patch literal 7589 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 literal 0 HcmV?d00001 From 9cc616e4cc539a6479673d62565625d446532b0e Mon Sep 17 00:00:00 2001 From: bart1e Date: Wed, 3 May 2023 19:21:58 +0200 Subject: [PATCH 09/23] Old tests removed --- .../0.8.17/CacheArrayLength.sol | 127 ------------------ ...rayLength.sol.0.8.17.CacheArrayLength.json | 74 ---------- 2 files changed, 201 deletions(-) delete mode 100644 tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol delete mode 100644 tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol.0.8.17.CacheArrayLength.json diff --git a/tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol b/tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol deleted file mode 100644 index 79858d1825..0000000000 --- a/tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol +++ /dev/null @@ -1,127 +0,0 @@ -pragma solidity 0.8.17; - -contract CacheArrayLength -{ - struct S - { - uint s; - } - - S[] array; - S[] array2; - - function f() public - { - // array accessed but length doesn't change - for (uint i = 0; i < array.length; i++) // warning should appear - { - array[i] = S(0); - } - - // array.length doesn't change, but array.length not used in loop condition - for (uint i = array.length; i >= 0; i--) - { - - } - - // array.length changes in the inner loop - for (uint i = 0; i < array.length; i++) - { - for (uint j = i; j < 2 * i; j++) - array.push(S(j)); - } - - // array.length changes - for (uint i = 0; i < array.length; i++) - { - array.pop(); - } - - // array.length changes - for (uint i = 0; i < array.length; i++) - { - delete array; - } - - // array.length doesn't change despite using delete - for (uint i = 0; i < array.length; i++) // warning should appear - { - delete array[i]; - } - - // array.length changes; push used in more complex expression - for (uint i = 0; i < array.length; i++) - { - array.push() = S(i); - } - - // array.length doesn't change - for (uint i = 0; i < array.length; i++) // warning should appear - { - array2.pop(); - array2.push(); - array2.push(S(i)); - delete array2; - delete array[0]; - } - - // array.length changes; array2.length doesn't change - for (uint i = 0; i < 7; i++) - { - for (uint j = i; j < array.length; j++) - { - for (uint k = 0; k < j; k++) - { - - } - - for (uint k = 0; k < array2.length; k++) // warning should appear - { - array.pop(); - } - } - } - - // array.length doesn't change; array2.length changes - for (uint i = 0; i < 7; i++) - { - for (uint j = i; j < array.length; j++) // warning should appear - { - for (uint k = 0; k < j; k++) - { - - } - - for (uint k = 0; k < array2.length; k++) - { - array2.pop(); - } - } - } - - // none of array.length and array2.length changes - for (uint i = 0; i < 7; i++) - { - for (uint j = i; j < array.length; j++) // warning should appear - { - for (uint k = 0; k < j; k++) - { - - } - - for (uint k = 0; k < array2.length; k++) // warning should appear - { - - } - } - } - - S[] memory array3; - - // array3 not modified, but it's not a storage array - for (uint i = 0; i < array3.length; i++) - { - - } - } -} \ No newline at end of file diff --git a/tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol.0.8.17.CacheArrayLength.json b/tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol.0.8.17.CacheArrayLength.json deleted file mode 100644 index 3323ff4791..0000000000 --- a/tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol.0.8.17.CacheArrayLength.json +++ /dev/null @@ -1,74 +0,0 @@ -[ - [ - { - "elements": [], - "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#105 should use cached array length instead of referencing `length` member of the storage array.\n ", - "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#105 should use cached array length instead of referencing `length` member of the storage array.\n ", - "first_markdown_element": "", - "id": "2ff6144814e406cadadd58712f5a7a8ef6ee169da06660d590e7bee37759fc98", - "check": "cache-array-length", - "impact": "Optimization", - "confidence": "High" - }, - { - "elements": [], - "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#59 should use cached array length instead of referencing `length` member of the storage array.\n ", - "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#59 should use cached array length instead of referencing `length` member of the storage array.\n ", - "first_markdown_element": "", - "id": "48a6388cf2193fdd780ea86cb3e588dfd3276182e209f3f2807d9927a5ba25bc", - "check": "cache-array-length", - "impact": "Optimization", - "confidence": "High" - }, - { - "elements": [], - "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#47 should use cached array length instead of referencing `length` member of the storage array.\n ", - "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#47 should use cached array length instead of referencing `length` member of the storage array.\n ", - "first_markdown_element": "", - "id": "562b7ae618977ea1d0a232d8af5edd81ce404b6844e864d0c4ab162a88142c71", - "check": "cache-array-length", - "impact": "Optimization", - "confidence": "High" - }, - { - "elements": [], - "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#112 should use cached array length instead of referencing `length` member of the storage array.\n ", - "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#112 should use cached array length instead of referencing `length` member of the storage array.\n ", - "first_markdown_element": "", - "id": "8f1aa2da0763f65179e90a2b96d7feb68c99aab60f227a5da8dad2f12069f047", - "check": "cache-array-length", - "impact": "Optimization", - "confidence": "High" - }, - { - "elements": [], - "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#88 should use cached array length instead of referencing `length` member of the storage array.\n ", - "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#88 should use cached array length instead of referencing `length` member of the storage array.\n ", - "first_markdown_element": "", - "id": "9c988bc3f6748fadb8527c9eea53e3d06f5f4e7b2a0c7c70993c4af758bbba47", - "check": "cache-array-length", - "impact": "Optimization", - "confidence": "High" - }, - { - "elements": [], - "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#78 should use cached array length instead of referencing `length` member of the storage array.\n ", - "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#78 should use cached array length instead of referencing `length` member of the storage array.\n ", - "first_markdown_element": "", - "id": "a1c39b4ae47535f3354d0bf26ea67d3b524efb94a3bdb2f503bda245471ee451", - "check": "cache-array-length", - "impact": "Optimization", - "confidence": "High" - }, - { - "elements": [], - "description": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#16 should use cached array length instead of referencing `length` member of the storage array.\n ", - "markdown": "Loop condition at tests/detectors/cache-array-length/0.8.17/CacheArrayLength.sol#16 should use cached array length instead of referencing `length` member of the storage array.\n ", - "first_markdown_element": "", - "id": "c57b54ebb07e5114ccb8c124fa3971b9385d5252264f2bf2acbe131b3a986aa8", - "check": "cache-array-length", - "impact": "Optimization", - "confidence": "High" - } - ] -] \ No newline at end of file From 0b7257209dd09b1d877ec40870dd55eaf8ab10d1 Mon Sep 17 00:00:00 2001 From: bart1e Date: Mon, 15 May 2023 21:01:04 +0200 Subject: [PATCH 10/23] External calls handled + output printing changed --- .../operations/cache_array_length.py | 16 +++++-- ...yLength_0_8_17_CacheArrayLength_sol__0.txt | 18 ++++--- .../0.8.17/CacheArrayLength.sol | 44 ++++++++++++++++++ .../0.8.17/CacheArrayLength.sol-0.8.17.zip | Bin 7589 -> 8898 bytes 4 files changed, 66 insertions(+), 12 deletions(-) diff --git a/slither/detectors/operations/cache_array_length.py b/slither/detectors/operations/cache_array_length.py index 1f8111bdb4..da73d3fd5a 100644 --- a/slither/detectors/operations/cache_array_length.py +++ b/slither/detectors/operations/cache_array_length.py @@ -7,7 +7,7 @@ from slither.core.source_mapping.source_mapping import SourceMapping from slither.core.variables import StateVariable from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification -from slither.slithir.operations import Length, Delete +from slither.slithir.operations import Length, Delete, HighLevelCall class CacheArrayLength(AbstractDetector): @@ -119,8 +119,10 @@ def _is_loop_referencing_array_length( # - when `push` is called # - when `pop` is called # - when `delete` is called on the entire array + # - when external function call is made (instructions from internal function calls are already in + # `node.all_slithir_operations()`, so we don't need to handle internal calls separately) if node.type == NodeType.EXPRESSION: - for op in node.irs: + for op in node.all_slithir_operations(): if isinstance(op, Length) and op.value == array: # op accesses array.length, not necessarily modifying it return True @@ -132,6 +134,8 @@ def _is_loop_referencing_array_length( and op.expression.expression.value == array ): return True + if isinstance(op, HighLevelCall) and not op.function.view and not op.function.pure: + return True for son in node.sons: if son not in visited: @@ -173,7 +177,7 @@ def _handle_loops(nodes: List[Node], non_optimal_array_len_usages: List[SourceMa if not CacheArrayLength._is_loop_referencing_array_length( if_node, visited, array, 1 ): - non_optimal_array_len_usages.append(if_node.expression) + non_optimal_array_len_usages.append(if_node) @staticmethod def _get_non_optimal_array_len_usages_for_function(f: Function) -> List[SourceMapping]: @@ -207,8 +211,10 @@ def _detect(self): ) for usage in non_optimal_array_len_usages: info = [ - f"Loop condition at {usage.source_mapping} should use cached array length instead of referencing " - f"`length` member of the storage array.\n " + "Loop condition at ", + usage, + " should use cached array length instead of referencing `length` member " + "of the storage array.\n ", ] res = self.generate_result(info) results.append(res) diff --git a/tests/e2e/detectors/snapshots/detectors__detector_CacheArrayLength_0_8_17_CacheArrayLength_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_CacheArrayLength_0_8_17_CacheArrayLength_sol__0.txt index a0ba357409..63d4b883c3 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_CacheArrayLength_0_8_17_CacheArrayLength_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_CacheArrayLength_0_8_17_CacheArrayLength_sol__0.txt @@ -1,14 +1,18 @@ -Loop condition at tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#47 should use cached array length instead of referencing `length` member of the storage array. +Loop condition at i < array.length (tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#36) should use cached array length instead of referencing `length` member of the storage array. -Loop condition at tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#78 should use cached array length instead of referencing `length` member of the storage array. +Loop condition at i_scope_22 < array.length (tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#166) should use cached array length instead of referencing `length` member of the storage array. -Loop condition at tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#16 should use cached array length instead of referencing `length` member of the storage array. +Loop condition at j_scope_11 < array.length (tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#108) should use cached array length instead of referencing `length` member of the storage array. -Loop condition at tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#88 should use cached array length instead of referencing `length` member of the storage array. +Loop condition at i_scope_6 < array.length (tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#79) should use cached array length instead of referencing `length` member of the storage array. -Loop condition at tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#105 should use cached array length instead of referencing `length` member of the storage array. +Loop condition at i_scope_21 < array.length (tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#160) should use cached array length instead of referencing `length` member of the storage array. -Loop condition at tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#112 should use cached array length instead of referencing `length` member of the storage array. +Loop condition at k_scope_9 < array2.length (tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#98) should use cached array length instead of referencing `length` member of the storage array. -Loop condition at tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#59 should use cached array length instead of referencing `length` member of the storage array. +Loop condition at k_scope_17 < array2.length (tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#132) should use cached array length instead of referencing `length` member of the storage array. + +Loop condition at j_scope_15 < array.length (tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#125) should use cached array length instead of referencing `length` member of the storage array. + +Loop condition at i_scope_4 < array.length (tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#67) should use cached array length instead of referencing `length` member of the storage array. diff --git a/tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol b/tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol index 79858d1825..704d6bed9e 100644 --- a/tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol +++ b/tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol @@ -10,6 +10,26 @@ contract CacheArrayLength S[] array; S[] array2; + function h() external + { + + } + + function g() internal + { + this.h(); + } + + function h_view() external view + { + + } + + function g_view() internal view + { + this.h_view(); + } + function f() public { // array accessed but length doesn't change @@ -123,5 +143,29 @@ contract CacheArrayLength { } + + // array not modified, but it may potentially change in an internal function call + for (uint i = 0; i < array.length; i++) + { + g(); + } + + // array not modified, but it may potentially change in an external function call + for (uint i = 0; i < array.length; i++) + { + this.h(); + } + + // array not modified and it cannot be changed in a function call since g_view is a view function + for (uint i = 0; i < array.length; i++) // warning should appear + { + g_view(); + } + + // array not modified and it cannot be changed in a function call since h_view is a view function + for (uint i = 0; i < array.length; i++) // warning should appear + { + this.h_view(); + } } } \ No newline at end of file diff --git a/tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol-0.8.17.zip b/tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol-0.8.17.zip index ab3d813718b45d438f91556934cc243e900beb57..dafdfc43167f9eb622fed98bdc69e91d02f89dfa 100644 GIT binary patch delta 8644 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 delta 7346 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 Date: Wed, 7 Jun 2023 22:00:15 +0400 Subject: [PATCH 11/23] local variable location fix (#1942) * Local variable location fix When we had a function f() which returns a pair, the result passed to local variables didn't initialize the location attribute. For example: contract A { function f() public returns (int, bytes memory) { return (1, "asdf"); } function g() public { (int x, bytes memory y) = f(); } } the location of the local variable x as well as the location of the local variable y was None * moved the fixes to the proper file * removed useless newlines --- .../local_variable_init_from_tuple.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/slither/solc_parsing/variables/local_variable_init_from_tuple.py b/slither/solc_parsing/variables/local_variable_init_from_tuple.py index 1a551c6957..f1c8728486 100644 --- a/slither/solc_parsing/variables/local_variable_init_from_tuple.py +++ b/slither/solc_parsing/variables/local_variable_init_from_tuple.py @@ -16,3 +16,21 @@ def underlying_variable(self) -> LocalVariableInitFromTuple: # Todo: Not sure how to overcome this with mypy assert isinstance(self._variable, LocalVariableInitFromTuple) return self._variable + + def _analyze_variable_attributes(self, attributes: Dict) -> None: + """' + Variable Location + Can be storage/memory or default + """ + if "storageLocation" in attributes: + location = attributes["storageLocation"] + self.underlying_variable.set_location(location) + else: + if "memory" in attributes["type"]: + self.underlying_variable.set_location("memory") + elif "storage" in attributes["type"]: + self.underlying_variable.set_location("storage") + else: + self.underlying_variable.set_location("default") + + super()._analyze_variable_attributes(attributes) From 473576994024940ded6d895290c7e881f19efa6a Mon Sep 17 00:00:00 2001 From: Tigran Avagyan Date: Tue, 20 Jun 2023 11:48:45 +0400 Subject: [PATCH 12/23] Fixed issue which disallowed using operator[] with TopLevelVariables --- slither/slithir/operations/index.py | 7 +++++-- slither/slithir/variables/reference.py | 3 ++- .../local_variable_init_from_tuple.py | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/slither/slithir/operations/index.py b/slither/slithir/operations/index.py index f38a25927f..4fcfb8a6d7 100644 --- a/slither/slithir/operations/index.py +++ b/slither/slithir/operations/index.py @@ -3,6 +3,7 @@ from slither.core.declarations import SolidityVariableComposed from slither.core.source_mapping.source_mapping import SourceMapping from slither.core.variables.variable import Variable +from slither.core.variables.top_level_variable import TopLevelVariable from slither.slithir.operations.lvalue import OperationWithLValue from slither.slithir.utils.utils import is_valid_lvalue, is_valid_rvalue, RVALUE, LVALUE from slither.slithir.variables.reference import ReferenceVariable @@ -13,8 +14,10 @@ def __init__( self, result: ReferenceVariable, left_variable: Variable, right_variable: RVALUE ) -> None: super().__init__() - assert is_valid_lvalue(left_variable) or left_variable == SolidityVariableComposed( - "msg.data" + assert ( + is_valid_lvalue(left_variable) + or left_variable == SolidityVariableComposed("msg.data") + or isinstance(left_variable, TopLevelVariable) ) assert is_valid_rvalue(right_variable) assert isinstance(result, ReferenceVariable) diff --git a/slither/slithir/variables/reference.py b/slither/slithir/variables/reference.py index 9ab51be655..2f99d322e4 100644 --- a/slither/slithir/variables/reference.py +++ b/slither/slithir/variables/reference.py @@ -2,6 +2,7 @@ from slither.core.declarations import Contract, Enum, SolidityVariable, Function from slither.core.variables.variable import Variable +from slither.core.variables.top_level_variable import TopLevelVariable if TYPE_CHECKING: from slither.core.cfg.node import Node @@ -46,7 +47,7 @@ def points_to(self, points_to): from slither.slithir.utils.utils import is_valid_lvalue assert is_valid_lvalue(points_to) or isinstance( - points_to, (SolidityVariable, Contract, Enum) + points_to, (SolidityVariable, Contract, Enum, TopLevelVariable) ) self._points_to = points_to diff --git a/slither/solc_parsing/variables/local_variable_init_from_tuple.py b/slither/solc_parsing/variables/local_variable_init_from_tuple.py index 1a551c6957..f1c8728486 100644 --- a/slither/solc_parsing/variables/local_variable_init_from_tuple.py +++ b/slither/solc_parsing/variables/local_variable_init_from_tuple.py @@ -16,3 +16,21 @@ def underlying_variable(self) -> LocalVariableInitFromTuple: # Todo: Not sure how to overcome this with mypy assert isinstance(self._variable, LocalVariableInitFromTuple) return self._variable + + def _analyze_variable_attributes(self, attributes: Dict) -> None: + """' + Variable Location + Can be storage/memory or default + """ + if "storageLocation" in attributes: + location = attributes["storageLocation"] + self.underlying_variable.set_location(location) + else: + if "memory" in attributes["type"]: + self.underlying_variable.set_location("memory") + elif "storage" in attributes["type"]: + self.underlying_variable.set_location("storage") + else: + self.underlying_variable.set_location("default") + + super()._analyze_variable_attributes(attributes) From e1febdd74ecd2c26fbf1524d872034b57b15e3e2 Mon Sep 17 00:00:00 2001 From: bossjoker1 <1397157763@qq.com> Date: Wed, 21 Jun 2023 09:34:59 -0500 Subject: [PATCH 13/23] Check the respective parameter's storage location for each argument --- .../compiler_bugs/array_by_reference.py | 13 ++++++----- ...rence_0_4_25_array_by_reference_sol__0.txt | 14 +++++++----- ...rence_0_5_16_array_by_reference_sol__0.txt | 14 +++++++----- ...rence_0_6_11_array_by_reference_sol__0.txt | 14 +++++++----- ...erence_0_7_6_array_by_reference_sol__0.txt | 14 +++++++----- .../0.4.25/array_by_reference.sol | 21 ++++++++++++++++++ .../0.4.25/array_by_reference.sol-0.4.25.zip | Bin 4879 -> 6184 bytes .../0.5.16/array_by_reference.sol | 21 ++++++++++++++++++ .../0.5.16/array_by_reference.sol-0.5.16.zip | Bin 4925 -> 6194 bytes .../0.6.11/array_by_reference.sol | 21 ++++++++++++++++++ .../0.6.11/array_by_reference.sol-0.6.11.zip | Bin 4841 -> 6086 bytes .../0.7.6/array_by_reference.sol | 21 ++++++++++++++++++ .../0.7.6/array_by_reference.sol-0.7.6.zip | Bin 4741 -> 5972 bytes 13 files changed, 124 insertions(+), 29 deletions(-) diff --git a/slither/detectors/compiler_bugs/array_by_reference.py b/slither/detectors/compiler_bugs/array_by_reference.py index 04dfe085a8..47e2af5819 100644 --- a/slither/detectors/compiler_bugs/array_by_reference.py +++ b/slither/detectors/compiler_bugs/array_by_reference.py @@ -133,7 +133,7 @@ def detect_calls_passing_ref_to_function( continue # Verify one of these parameters is an array in storage. - for arg in ir.arguments: + for (param, arg) in zip(ir.function.parameters, ir.arguments): # Verify this argument is a variable that is an array type. if not isinstance(arg, (StateVariable, LocalVariable)): continue @@ -141,8 +141,11 @@ def detect_calls_passing_ref_to_function( continue # If it is a state variable OR a local variable referencing storage, we add it to the list. - if isinstance(arg, StateVariable) or ( - isinstance(arg, LocalVariable) and arg.location == "storage" + if ( + isinstance(arg, StateVariable) + or (isinstance(arg, LocalVariable) and arg.location == "storage") + ) and ( + isinstance(param.type, ArrayType) and param.location != "storage" ): results.append((node, arg, ir.function)) return results @@ -165,9 +168,9 @@ def _detect(self) -> List[Output]: calling_node.function, " passes array ", affected_argument, - "by reference to ", + " by reference to ", invoked_function, - "which only takes arrays by value\n", + " which only takes arrays by value\n", ] res = self.generate_result(info) diff --git a/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_4_25_array_by_reference_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_4_25_array_by_reference_sol__0.txt index f056bea101..5cb8add39f 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_4_25_array_by_reference_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_4_25_array_by_reference_sol__0.txt @@ -1,12 +1,14 @@ -D.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#39)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#25-28)which only takes arrays by value +D.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#39) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#21-23) which only takes arrays by value -D.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#39)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#21-23)which only takes arrays by value +C.g() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#11) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#25-28) which only takes arrays by value -C.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#2)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#21-23)which only takes arrays by value +C.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#2) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#25-28) which only takes arrays by value -C.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#2)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#25-28)which only takes arrays by value +C.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#2) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#21-23) which only takes arrays by value -C.g() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#11)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#25-28)which only takes arrays by value +D.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#39) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#25-28) which only takes arrays by value -C.g() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#11)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#21-23)which only takes arrays by value +C.g() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#11) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#21-23) which only takes arrays by value + +E.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#57-61) passes array E.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#54) by reference to E.setByValue(uint256[1],uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#63-66) which only takes arrays by value diff --git a/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_5_16_array_by_reference_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_5_16_array_by_reference_sol__0.txt index 4264c809aa..6e97d8cc2c 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_5_16_array_by_reference_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_5_16_array_by_reference_sol__0.txt @@ -1,12 +1,14 @@ -D.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#39)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#25-28)which only takes arrays by value +D.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#39) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#21-23) which only takes arrays by value -D.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#39)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#21-23)which only takes arrays by value +C.g() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#11) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#25-28) which only takes arrays by value -C.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#2)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#21-23)which only takes arrays by value +C.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#2) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#25-28) which only takes arrays by value -C.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#2)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#25-28)which only takes arrays by value +C.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#2) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#21-23) which only takes arrays by value -C.g() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#11)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#25-28)which only takes arrays by value +D.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#39) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#25-28) which only takes arrays by value -C.g() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#11)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#21-23)which only takes arrays by value +C.g() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#11) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#21-23) which only takes arrays by value + +E.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#57-61) passes array E.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#54) by reference to E.setByValue(uint256[1],uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#63-66) which only takes arrays by value diff --git a/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_6_11_array_by_reference_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_6_11_array_by_reference_sol__0.txt index e71930b514..39574b5f5d 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_6_11_array_by_reference_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_6_11_array_by_reference_sol__0.txt @@ -1,12 +1,14 @@ -D.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#39)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#25-28)which only takes arrays by value +D.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#39) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#21-23) which only takes arrays by value -D.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#39)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#21-23)which only takes arrays by value +C.g() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#11) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#25-28) which only takes arrays by value -C.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#2)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#21-23)which only takes arrays by value +C.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#2) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#25-28) which only takes arrays by value -C.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#2)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#25-28)which only takes arrays by value +C.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#2) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#21-23) which only takes arrays by value -C.g() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#11)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#25-28)which only takes arrays by value +D.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#39) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#25-28) which only takes arrays by value -C.g() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#11)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#21-23)which only takes arrays by value +C.g() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#11) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#21-23) which only takes arrays by value + +E.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#57-61) passes array E.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#54) by reference to E.setByValue(uint256[1],uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#63-66) which only takes arrays by value diff --git a/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_7_6_array_by_reference_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_7_6_array_by_reference_sol__0.txt index 7c0f9ccd90..74ea36a0c4 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_7_6_array_by_reference_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_7_6_array_by_reference_sol__0.txt @@ -1,12 +1,14 @@ -D.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#39)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#25-28)which only takes arrays by value +D.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#39) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#21-23) which only takes arrays by value -D.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#39)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#21-23)which only takes arrays by value +C.g() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#11) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#25-28) which only takes arrays by value -C.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#2)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#21-23)which only takes arrays by value +C.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#2) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#25-28) which only takes arrays by value -C.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#2)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#25-28)which only takes arrays by value +C.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#2) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#21-23) which only takes arrays by value -C.g() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#11)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#25-28)which only takes arrays by value +D.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#39) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#25-28) which only takes arrays by value -C.g() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#11)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#21-23)which only takes arrays by value +C.g() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#11) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#21-23) which only takes arrays by value + +E.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#57-61) passes array E.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#54) by reference to E.setByValue(uint256[1],uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#63-66) which only takes arrays by value diff --git a/tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol b/tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol index 304af6a488..c2707601a0 100644 --- a/tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol +++ b/tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol @@ -48,4 +48,25 @@ contract D { } +} + +contract E { + uint[1] public x; // storage + uint[1] public y; // storage + + function f() public { + uint[1] memory temp; + setByValue(temp, x); // can set temp, but cannot set x + setByRef(temp, y); // can set temp and y + } + + function setByValue(uint[1] memory arr, uint[1] memory arr2) internal { + arr[0] = 1; + arr2[0] = 2; + } + + function setByRef(uint[1] memory arr, uint[1] storage arr2) internal { + arr[0] = 2; + arr2[0] = 3; + } } \ No newline at end of file diff --git a/tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol-0.4.25.zip b/tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol-0.4.25.zip index d9daf8f4d7dc27c4f371b018a31cc9736180c278..96bdaa0f85c484feec1e0f47ea8e3409f0cd1dc9 100644 GIT binary patch delta 5938 zcmV-27tQF8Ca5qQP)h>@KL7#%4gir(%2x0kl?-wh005dd0RSqK%n~h;NXmb$kYnrz zQqMh9B8y1aD8q1p=!{taw}#r2iQzXSX}N6ke~A^1}I zy&B=dUBc4IC1R>P^Cp{Jq%(isjgr4?^PnOoSXjpnCdj;QQru6L8{?;DtOMd^CQ~!& zsK5vVj`lsyf`FPS`}OOq8VCXi|LdBTrzC+mc}%{`Pv8XxHHi zzj8Eh>RH^qlKqpoIVk4c+Qi&dDo7-6bpf)OEH7%Kj-13mnohbkP8FHo;_b_*M zs=MM!yK`-MIgh^^&Vw(IhPLOnL($I$V6QVZBK{d|C3|Hu@I3G!s7a+2JKu4e305S> z+4PO_#5oK_!Td$o+l+r?xBSc&-`)Dq^2Oz7GGaOyZosWI_m)b-rOwEi5hOFmMfk`0 zfM;6?tISVFE)acbdk1Z&!FKZ)mbd~I)SQD7x7KS>K~mWLFSG>T;V&?Z?YzY~+%$se ze zA1O{(;QR_^CccwkDW(C0;e9BxI;i;NCj7LhGl`2>q8KJnYhm0Z@98)BA}i)=g6N;; zry1(Ig^v~uJ)wWnz_09T@Fi_<`nm;v>P=+JWEgl{vnHic98*4@btiNCekl)>iT1@( z#1{PGi4@t76Qtx-!t4ZmFe4Y^kV` zgszj52-AN+YbsY%6MAts0y=}soJ1QpPYi(a(G-wDW8q1j726G&mw*$R)@(7T&hvIs z)vW-ErUP&jpi!Gu3;Jf5rA+FRvrc?@RO?DpM>H_Rop{oGI?F7PAqJod!knu>m5kZJ z!sO~a6m$a_Q40(!!|^hoidBp8jdg`pejwaziD`c;E`pb+H*KJ;F4J5F_AIw0RDJo$!VAX~xfdL*O0ZW&`UfHMVgko6xkZ0?{fZKN+#S=!*@b$uY}|x`toMvi_O5>X z`<+=2kA~{$KRTPV8{Sq5x&gc@#!Kkmg&moe+Nmt;0n&7Y(!M2EfIuMRZ|`jaF}0W7 zo+G2OCM84%Jl?-t*nFB{5YZGAqG`jGTvEpqIS?;!tu=C2jsNe%s>p{A*MjEEf?GH|MB2Bo zB=dDOBEFHQFH>F=d`KtB%B$&>db$oJOL1chD5-D8X$KSt+Rg?BKnCk~q_d^`I!NtP zFYyTLzDh5$+{}983IQln{b4*cgNPdZ2^_eOwQ!Se!GpkvLXrM}MWp}bbdG{6Xb^r^yXpm@LYf_epg@!HK=3|G~DD*14lV#n}5j+aj@y;#Ht&>so> z(8PNXQSzXslDm5#*pB|iZbN_X4L!DShta>IEdq0P1Qf^68qflE&%@-byO1Sbe7`9g3h0nEX?Tar8OSfeE7t~PKjklc2FK3R}?Ig=q zq{{lnr)~1gk^HFf!JO+74K%f45~F1r;#xK4*(zjC%l)z^0%%43J7n;E9VM20%JJSn zY!AM9_P|U_Ft`0gT50jYF2oJBBh=n5uz?><~5gISk-JYz_cLAnJC@3Z4!hUMIT zM#uk|4XNB;y#^0kz#SR0rf(>SW$6DZo~};DZU#)H*3@`=PH*3TeU|g{>xP~2X3u(7 zgk-fXn-a0}Hh6yhM>2t^L=p_wHCM(ZtqoVNsQkOvXi)LQI%uS_$aZs0=cHy z6*}4omt-^KphV$>Mkxqn?jF2%JQsQ2w^(O&zp5Th+RA@tHyDK4*&;o_N`5anOS-Fl zkm2NU^yIlAV_IFjoM*)a3jax6JR@5b%VEYOa8^D14J=3>W23|Xm`)8m;a}edq6Ps5 zX3yjz8Dx(}Pc<$*Sew}-kelEFh^?VDDW-n9gzC>`UhHDF>hvfHj$oO`RO?fzlkpl} z8i5NB?&E(ho2cVOPeDvv@MeqgAk1R^#ZW>_lah@Rz#IQvxbqBtp!XFuUGMi+Z<99l z+;zMU9AozI_=dNH3JaqTqQ)l`hH@s=@bC+$+&0~QcWbN^l-FA@g>kS&>B*xa5ek#* zx~Ou)Z*)}3v`p;`fu^3Hi8wBDSLEPepi3OQQO+-x&&D==viXhgAO&?*gCOI`*UNJope3oF#hZ^QQA3npHqC$ML>8^D8Ke4G(qb(%{ktp3V_TA3Wb{XR zzGNXP(XzWqW}Baqsa@h3*|@C146V#3V5Rwu`vrjaAvT8>gv9DAI@CHslx)RByua>3~rw1Z|NTRYiO5T=mA z3u21O@wNP7?BK5MUsal+2VC}B({R7+(55R&>yIN~bpzz|rC)j$!$;Cbl{AKTE|yX- zsbg<*L8Tlb#mnU{Cq6;ZFb5nm3w+0fUy^F@76@#;tDfv-X#7`tIOZk6iwOF>%fo*q zS6gSGsuoLxG-nYEd3ZgIx0$o6o_t|9(@UFduCTN#e$Oa` z(&CEgkb44Povcj@X#WlOM9;VciqwDhEJrIXwcSVtHk`KO(JG4iZEJS9hggdY>enu> zJUW-Qmq{b5KGvLcYa6Y9_SCXTyq^SIn^Jf-@gzgKYhmj3%*cFZq;}tDan}J>^7HCQ zY0jbuU#%}a+0jllWz7#Y;CuTjs-5~P1j>(^kh zKY%oA$>hCYgDmVbCu^SaD>Q3vTQC5R3_~10`t8jJ!uPsF_={hc#}vVdc>2ipDd$6M zEvs*qTG-f!)ou4I%0<5Tv(W?gC1^?c9lw1;+~WR~&w0fR-04Xf&uFi+-T3LIFvB(q zz?&#*w7(;GS5+(y(r%Kj#YulIL~O9hwg{V1OTQ#VYV#3SCDJ)qo2@}0=G7O4#=Kgp zL|srkVd76#>0JqJo$25V-3uH^Dx(1E^5Nb&o!^vK!3)oiCP+oVPkzf0KQ;Fp(zF4x zh&*0s0%z4R=spAVf3;kM0Q;+njpMOi;PpR_nQ3WHR+BrQMja)@)7^i5JRoDvKDuLW z<;V<)L%_&A`LC2_q;#1vv%j?-YoPpREfojzI0h!x>J|Dt4!@=i_Q;v7#jSgBUwjZm z4FI=j_9y|q@*N*~=2x+|a|M~5Aq0jfJ}b9~I!FCq9lp=gXqmC*I|SUX!mDWPJH9iX z12kqh>=f`4czLSvtDJus2z!|_AqcS^1@de(&X3kVa0k!yor<;gebA1IggaoIB5l2* z#CC}CW0_>f<&amc3Q=As`^>NoEIGFJ7)x~$5^ratax~#^Eq6-h-P#mO@p2mK*ZGMJ2jdQ0)T>8FZ+JcY+#v+`p+Q@WRlR)YjK2*(xUQ zPG&Y%NYfzUD};u&um@-=TwoqQc%mGeUOg>x9fb9WM7Hl#2HZXZ1$>aX9LT4f_~1ou z6$wOU)bWimuL|#jZ=m->%_6*GK|y3s_%6KrSZ6h|@4bI~m)7fH+V|3OiBoLar1VeW zfZw!pa(1U0&Ifk9nBOyKa&*{$gC;oqAh}P04!IGOGlq3zd(rO@-EZ=l*n&O=)Dx~S zZW@E6rO7P7M&cK98YwNnMiv60(6ig>vsOz4Ta7f5H<>HtTnQ7pF$H{$YtIG*^wWqvT(y?kqy8^#_nVG%Ld*i^#?o?PuWN-HYX()mz$27?&Q(3Vb0~n zdrn_28Ewabzz3ff5o}NvrKOEdBjepr{&E4WZ#Npk*pV9MhWRrNzDO{1 zOK5+@m>UXn+XAyfy@}eN@+WegwU@-7hi^YQNb7mK!?mIeL$+eH5rJ~kgy{q@k6e_D zC(Qo}rAyN%mP6n4YK=s=h=sZyRJ}1RB`_A^uvrrTvI_oYKJ8DeSJ-P^XuBg5F*P`! z?N6}9>aMfSNDc7cJrUeQ)7L+-zN#lXY&Cz`H&agbZwd*B(LGTFGp!yw1n4sH;Eei! ze$Ft?E^MKPiv63Xpu67Z^;aCI1Ljua2 z_hv}aiw;v8Ay`dug`D?bSHze?yW6yYPLRdt!LRX}7Tv+41NPyx+G?r}g9;?raXLb) zlJ@wWUaK@KS9WsDmXd}_wut;;#d&`_@}p8LyoMWGnz6_C&f+3Zf|9P?V>X7&K(3}j ziMc)zfT8W|UQJ-5a~vgG{zUG+RYY7~wT(5iWyO#+ez<3;73GafK~@jUC`JmUEo;xln8$vvTq~l znu!O`siV+^mm^N|1cOXC`;Cv^YQJ%uy$Myn|ky@ zm7JHbH*aX_NK2tDT~lPjnN!DARMgpM6oia=9%fcR>`3DL6faFYUoDUI$H9-ZA5Gf&k^ zJ_EjwVnX8(N4Oc7veCwzj z1^~{=ra^5*+Aw1=F_R3v5)&#KpM{7|(!WH=-RoK^1DP6q8q1^3S7(2Ya-(_dy2FFs z-lEoYA4|~ts(7h(f>24;Ty!Xa+gvq74-q4|hE6I^Cu5S{{rtRost0(p{o$YtXZZJg z|K@2<6s^*D83gc#cI}+CAYl(({PaP#?*ufP4L0+*%Az{`qqZE|+|`aH&4XmRMdy}{ zqMlD#7^e8j$GP6IWW#^|FyRIl%$IvZ41nxMh14q|>nF0+ZSWs2}L4<^KYfg+;`s5uPM$WEEU6~P5 z`KEN3ww7a%B6x|Tk+SNw0_VGF-Y5+M==S%iciu{l2e26G6KcO)1{NQnzZv7cZNaH- z(FzceD9QoX}N%2be#gpv5Xt?@}uj7B%vw}-a^_|Z? zSCn#o5HyKdpEqAxhE|1iCio?vhOpAQ0%j3uz46j7ZM+Y4B1h~?;yKhuw(aFnEfLKp zF3@h5_Kc$D;vbMi@`JTZ=g_`7?H67s5>?Vk%n`<%ms)=%>l<7T$j*m64fI%W{H{OW z@oOyqcaU@F+A`DovQ)I~=O0NTRv#1)6XXqCK#Ar6;6OlEscy}@&~~o$VOVG3tit(* zl&6MGQ)&@~SfkDVleh)LP)h*@KL7#%4gf`scUI_!lM7iA006Sw0FfE_f36UlSezi-=uRHY z)79$t3M%3c#a3DPw%6Mr0OWun&;7k`z7GIp`mYKf?orTNE1mF^5SyF@^E%3A4Il@a z1j84D!9uq{lc>$OQ)Lf}<))WqFpeUnnPGDJb;v#{-U!ih3qi(i$}%WLyd)D_pJOnz zk@RB9LirfF{urg@e=YHWm{a75^7>D3M=ah-g=c(xl#~GPiTKsYj-^(O9TydP#|MDw zvzjvG@u?Ya3z|hHyZ|iW53#DJWk`uPpPh|w%RpmF^XLMvX0p$Qgf1JOy1%ik`}eW! zbIyH}6}@x~P*B=#5&^hZ1~Y*9;T=+6^nG+1whwbJ(t$k~e+;7`#spya`;?2OJ;~S% z;0Bp@oYR|5c2eFzTHX1+3Z9Uj^c&nUJ^zzOw*UDd^X@{Oa)RdS7+STQ2)bhqP4x`& zbggA)89i;@^~|kR!rX5yByZ(!#hPWw#rgZ`pXu+aif8N)0PZ%zSb@gVwYad7q44Wg&?z(I-Pf%3_CP+=JZUVnlX8jfVjJ zySu)zQg@=O`aT=>7wPm_5a#iW%)f+0{G3D-7aQU#f4J8UX(YmrZ!1dii*8f>U!Q4G z%poDot7@;*%Qh>Bq&sHhkQ`2A#N7YSF&3!YhYmJ`Z(UYpiG4Sb;Dy9Sq?|O2t3U@4 zOk5Q|Vo9P2JC6TzZr#nu0FvNu+Nvgj%unCHUZ^OeiqY=&$$O3~hA4-{XhzQc0uB3o zY4f3_O4l0NFoM9FkPLa1WHSzMl8{1rmH zm>gjZD2aC4h2~+WXDE_{Kco#4U+!Q!D5Fpe46ta|C@SD4f_Q2Znnajw|Ju=T9P+Xe z;R8rKvx=+Icep9)Ouk~a)N1fM^K73Gs!>+<){`M0keZ;sYG;Qn-O~Z26!|;;_Lu}b ze-JjbeMyt6T-d02@AhrF;PI*7UbcIbVBeKmfJDk{Q!!@#c}RX}UjI-KQZw_ajAgsB z2AVrgw9Ym3Y7Nu;k4h}WOzyp6F4PX!Rk3Z!X>BlAAnwHbSK0K`8 zK;xJW(mc3|@+e9}ZeapEn~+$v*bfs#f36W7DRsuLfe5xBvo8)pt@AG!IJq6IYuul# zA>)90>w|Ry+;H#sS{~hhzF>=Cf{y410pOXz_i~3MA)!+zX4MdVjkx-btvP`N(FFw| z0v>H%Ux+a|eTJ=V)rJb3AFL#WWjA^kIvGON&rWntx;S7DPwfpu*uSN;O5S;Pe^|OZ zW7esc4kM@HfAky(E)xE6zxl#lXc8xpF!qaXtO3;$;V-z!k7sWevyR}?J(y#0JhTIm zfA>``=RJ_NQixb7VGWcLu{%y5e9~!~Iu^d)Jj}xNY4$AO-ULcQU$~9GxmR@=Ti99_ z1+xOZ{XCORj{M@?&0Y8wL2q;bf0Se>pP3q=u&@6WPmw)MO-SZ8f(*&_vw{I-Q9{qf zq>jD=K_e7pUf<;ns_PR~r^XiHQhd=~Gge=r*xu@anH89-t^}a}mE=>y(0{n*v>t@A z^GHU-t%4X3PGo?8g*3TEK<4>T(u|$zvd7pwWBNnm+YbG)lBBU4jdaU!f1o%SDltU! zKrfEb$+Y5Yr`cC-lr?U*Ev%X|%U$y&BJuewPj}C0Gbr`Tp23ZJo`EgRDyea;m) z*7p24vCEWD{^4kP)kt{Vp!RoD@-U-|bG<)z*Je)7SGJFp^0*6WBcm9@$}l+H7iuOH z+Jj>rHPZ{{GI>FuGS;}ne=nNGOFPr;y(3^gAfnN5vfO}>i4AVbhS;9n;rb1#QZMl` zo2Q_P*+lul>J=1f4AE~?31d=O?H=oh!KUc7lI_3!eu{bAD%ExX^% zc#D`+ZTpSBQ(PrI zv7wZsU)f+F(2rF|vI~Py)N1@fUJiU|&96=+pSgtb_30j*K>P67b;<~7*GD4yp8BzK z-A5errBj1e;EXKy2X%_n9x^LI@-3-$d{&Is;_f*Sbh7$`@d~1ijdvcsz+2!`r2UYN zo^Kofo>`uA_|Tf*e*-)h`MsY|5G-YCO(f5z6#h5B z$<~4amFb_tif?XO`oXrdR#=+J7bdK`cA1!Kzq<#6Rj+0(LEI{Wy)$#6fAK;2Hjnng^QyMgG0Q0I zL#1`@hoiDI?QARZh5_MGvej|{8#X&2RRkyeAtbc|uu)*p-zo&DpfxMZX zwZF+oYSFcP5b_0lQLS?Cp%N}(e@E9$G zff+V*yp0$&!ZQ@&2(t#*kO{ICWD;=Uw>UgV8yT4cxH?U9R;6`0r)PV@T^e;!#KWld z0pN#TNc%>(_~3_HjN1nqcdZqvHICU}y=Y%qlE;Ize>!#k+tRco!@rVycFzZM17Hu4 z1J=&qANpNz+&{%(-LOn(Ux;qgf4~n~DNLFO5n%{uPQPf4-u1TnNYopnQurzb;{rf` zP`qs0*vHqqcheqK;fBh+v@g)fIOD(-z64$k=T0W<12dbol=D9hEH#<(c9qBWTNK))7CGw||0jp61f}mIX%J^WjO)`vgQ|>o z*7lac#O@SCKd_t59|`}ngweC=&cVWEe~xcUPp%=O+uc;9G~Im}z54P^F0_U%#mmH! z1L2Td8Igs%v`C^?n#%PZCs_zg=Gs7b`@Yw`%1{Af0p&+|Dn|R)Fd8HzQLSLIXk@c` z>SYIP4P1l8d?fmx2Tds;iU`BK68g7}Wk3d89le9uvYy^cWlXlK)iG%lJ-C|yf2NmO zAD<}FskpS!W$BJ8Fm~WFF8bE*bF5cdrUXcb+FVF#Ii=GWQhLa;k=1iaxz=bRgvo8i z=}Ke@v_l9P$y2+`v#atb)Y%4^(mpA9O__XACa{$`ZZ7?)bYh+EnvB59f5>uAEGQ|x z^#ctYcyFqjlXe)#Kz%M7xTjdae?um4B?(7iQXr)FuIsE}j*=oxBL>xm?`Qa=K+r>= z&)u#!PNxRS&+7nHWfr{4)n(KdR?#LL0j+eoE0&__e543E1B|fTBV%7e7+LmQA97Ns zeh|9=4AtN@nVek(f8Fs+gAs&soWNRJ4^nS}fo*W5nEjO)xWp4Xpj9^CMp zKYqzF37A?suQD2kSdeIepSQ&BYb9rFb4b5O6O$5djSn-1mR}jpS^@ZR&X1l_Rin*E zFj64TBzZ;^3$Y@eJIue*80PnU3KWF{ERIJ5y#97HV~Xm7>bjK)e?RL?qz(!ufkiw9 zmlQi#89I*dzB*&W8-s{36!w%1sS?IHUJEMCi=@HktihDkEvKHH{wB&SK1C=jOK z*TE0LDG&%<8xqv099L)NIlP|;4 z3ffBjO7aIcz1?`i4m%H!?F90FgO43wj&>yu_&+_f(S?yzViVBCs#7LQU}p$(DS`vJ z3K=$HkRWs|qe?S}=-ZnWjfW!U{yc{8rL>*830AZ*<)YIDf2rUJ*X7JYxSS60I9G-? zkHApgS{3od%$UhWIs@9n152+-aKy%z#P1U*d8i<(Sy!e2O9sv$kT)+pMdLqu7zqUr zA%p=}tSp5$zM!CG-laXd&u3N#V%QH`*cjUZen1W;U$2@CCj~e{VEdu*+t{a}`8X-~ z2Yb*g<-?B}f59SF2P|Ap&ynhvWq9j8RA;O*gLWmpocb?BhCYi4pe?wbcQ|z{U7&a` z3ixq5JZ2jk&TX!u@ov}k;xxEp5X?^gWMQP24NX^IS!~wr#~1{*8)$x>h#ME`u8g(0jH6>9S0h{aeEI~>hA3%-9W<@*NI{UoFp-GtEaQ1IG-1PhyZ zP#kh3X@j-5^u!kjMe~8V8Px;zvBM}pf}X%!MWlu;0Z8UT2h97xg_Zw!_sa&eb-#w` zCB;)-f4DkvTvJ%M;NIVWFM-Y~v5*}|tIH+nRGhyNy(N(6c)GO5#p>*QL|tXGUxKuk zv;KlCVn-0Z)I>X6&=&T)8ZtANnl`o5vQ5yHs2#C}R-Err1*7p1aT{4OYX|&6la%p7 zx-y$Ka&#lj)=bEZ8CfQML8v>E3%_U_oj5xGe{eY*Y9!I@R972<(~iAjZi*m|#^ ze|85{8fbes4tTT56-)_64g348?Wp>|Kv`X9upGv;%Q?cEgW#hJzh5c;yB0fTD{=XlO=9Uv{PAQSP^~$LBQXM4B5>My16!@9q z87Lj;PMTom*Fb))XLNh$Czxq{9$Dm-69IoW+TFyefh*ys5%$Tvf-?C3?4s?BO{lFd zjT(J;Ip)>S{O$SQvDp+{T7gKkM0e<)@m1c#tKO`Ktz9^5olDF;*KGdxO?(qjO928u k13v%)01f~}jdxb)hm#9g5&!_Q+yIl+7flAC5&!@I07d%U!T@KL7#%4gl0n%2uM*s91a#002%l0g)N{e~@GF6n_Sg_JGxn z-hV_#gJg1*otnvK1T62=Q0`(5$h{eY5Kl3Qq!AD?w;w3*K@D#wzMY-dn1nk z8Y#ss@~UBTlljHQW^(^UKAkn&EL0PdUxPE>j=%V>e>G{$#@}&+#R*OV4-TR+zAL&y z(!OR5U(BC}8VnxmfA__2Z-Zf_)EZAW)}s7x-*lE_2;YL!zuiI}L#5A0bvpX_mUfmARtsCAXo&=FQ8D-MURTn6%?uVGe?HsVLtO%Rv zDYRqLv!o{>BUjBK_}?cMt-Tl2wGZ`fcYnf+QrI5~+-a3?e+(dkPak=6$HaqM=$GDW zs^nZ&pvSClrt`E7LBdX&Rz7V5-w#cfm`Fu|Fd%qEyUDmR{s9~gih`1d{L>7<{U(xw zffVpAph|JG^q{~Aub44=5}xl83-d0yW~Py-sk=_1a`jCcnYc{O8G~9VvgfKS_#qR0 z(lYhN1u)Ngf4=X(I_n8UiIoXvI7JEQ2$6ol;bHqdNx|H?hrGuou)03b+0zjAV{4nL z>R144^kRRve;%cwx@4^5-+R*Pmy7`I$QGL-9bBH6l+}rusW1=j(mxD}aH=~b)%%1> zoN{m(l@#tuG+4MU4bT>R7C@9nd3_y4{xt6r8{*>|f5eC9O?842-QV%Hx3?9;kz;&d z`;IN$)@86MY4LPvghB}q7?PV}!qsoS1G9*xRR=nd7M7jLk|==gCz!f>xVgvYU!qed z0jB-8?EM;>E9gpd`;Mj^N*%M+x(M7r7r%#msFyU**~9Yg8Snhw7cQa0=?PYrfgH|o z_$akUdHD{*0f!0gjK#o2CPBxZ?a=Yfvl1+$I3x)9fr?I{h2V3D;ucI*?%^Kul~3#{ z6)X6lTuo^#bFx}~ZJMCKYA0^xh(w0N&lX=~%YOFRputv51mXxwT>;uqgk5&UHR`4p zf6J+vJYBZAeNbBJuF=A`Ijh%3Xb!Nj)e!~$$>C;iB*v3AW;6LHjI_HdKz%hrj8L(a z|7{rSDn1Z3DmG=kI2g}Q7@pu%iy(#eQo&dK_44&?cN#-X*K;vK!&i3OU`9KSs2*?< zTpz*vNvEmiNJN*n5u4-6|=%<;`NE85PQCj1W6Uvy`!e-B{N zbrvp_eqU`}TU9pwAk>6MO1na3s0?waV%$@`GEUW7H)nra98WvV1mvVyg=dHMwhV?e zf(s^brG2)8a~K66Yb9XQl1*n9+pIy{ej)kVu=}Bg>5&GqC~y(cjEVd3Ju_QhRzxwY z5{j_iH|X-Ht-xzPKpqA8uDuU0f9f}gqrXvEm3__tgcNcJwwI2+EncRV#P3AnzgMV; zk7|pE8c)*00&${#6XzW@c4l`H*UK}U?U35U>Uu4wu4WJR9ReB|tlq}AvF>)=;lar% zBkvmZ)OHem&qVT@x_!OSwm;9sudsnzlb__kXn-8tx0sDG<2^4z^C_4Me^GQDu13A0 zhuHy^Azv--B)p$9-EiGW9a=c{w;x19HPOPI;vqQ2-Z2w!mPqK6!plD$5_n+bJNeRL zZ|yHAF5v~sF3y4cn6KJI3-mmj^**A+sE8Q-mIB9yxp+d7DI9!MCOWpYCy!v1El>D& zkB~xp2X~INNH~Mz!xen{e`g&Td8iD{8H{Wl115KwPZ0!ng~1;JM;ZuChc8Z9bJGJz z3qC~%*20cJeLYB>8>^T+YPD;=S&aW0^H=Zw2?2KM;aUUa+%u8Taha)*hrsa!C76ZL zT+}-GK09kbM7K8g5++nQUu5*=Mf(d!8i@qIGP2`0IyRj6wD;>Je_e_oK5w3H_V`xt zi#zkhH5zL^tX6h5;(FaG=N9whs!^daB7Ni=fX&6*igCRMAl&~zsquYm(qqFXCRMAJ zQhqQB?A#!ti&U3hz<=G`)6JPT&oVNXv%;?OifnU#DBY(@uVBw0E}pH5({ol(ZWmJh zS9BRuxQRdh#}kwZf6gQ|Xq#DoCX;W^IcnFC{?T3rwYkyy3c4|#@x4DHI=GD$YX&#L zsO++yH^bE130Q+&xeuJ|Kg!ZR60RmCei>yu9_~F7EYf`x7 z1@5#J-#7!xih?UpHlJzBI2-}h!euzK_FxDve|oa*B_(g|JaBY_{sjegJbE;QLs~^Z z)*wHA81M6%e}w2ktc_fW_NQ0THhM>tDDb+Pc&(m(v!X3lX0C7so-;>(m_4P566$=l zqZ~WD^9CvI1Qb&gymxF5_nlX7s6_nRn)GV6eEhu#m#B3zz_GysU)B1h4Path%a(ip z1L+j~g>~`bZXNChH8)f+u-d!PJW}8k4X3QTDw7ozf6i^?(53tKp$_-eg@RZ3PN&r4 z6iRC<2|Fv#WZ>S)iX80qVx>a9^EhcL7%5}!9K}evis`*6=QqpHuoG+Gfc-r9ldyqS zNFz~qpyCOE3=btKPwmzXvHSn|fS*mf^SRyn1DU%z-IQ3i)NAp;89a-@!s)oofsQ?e z>AF+we?&eXK4iPNTIulXh0qkA*Z?k#3$y6b2;o~zw&t9FX%xRgcreW#P?7UJu}^&PbJoug;0tMHA$a ze}}-CJ#IMTXYv?wp|hmS(ee&k86ow|c#1v#JXR+a#Ev`89p2>N`~2<)%yo>%*1cJJ%|F%=jPmnZBTG_FlJjyKmca~WkIl0Y*6V;vw-u@GQ-%^jx30`;_*=R2%Fibv(Mc%X1p?s{fBDJ(! zk>PoSY5&!s*z2r?eu(pX>12n-f4f3&_U&ECYKLB|YMZ=O)1~N0BM?`|e>{v)qMksw zE7E+49)Wp0Rox0H*J3a6MXWxTVd-U?ayTnQ-AM9(Qc6IMFd^XN;des~RHg>yV0Y9< zE9IoHgTKbGxHvaLJt(s~2zT2_&aVE@5z?l1OAiFVg^GG2u9F|J?O0w*AR`H?!_r6% zPM+wAw<}|1;0+o-KoLoUe=3Il<=L#2p1*t5d^(npyR6A-tPdIRSpO^{P!#zJvgW9* zSD>(~n7Jr)=B9r6+BnQqRYIsjbRhH^e~H6{vv+IdLD@vE^Egwre|z$BHDU!9auS3M zyI(mT-RlfKS(`_Xg+Ei1o6We8^bq$|5u&&TBk?iB4Z&+-ig*21e{(TSTPTK-wG-6P z)7NZkp66HUt=n?`GRvxHv`z56yNQVHyK2L7%(oN@ug$Fimn&&cV3yD9HF)65P*;<8 zS^ff!yf40atisM9fU;MB1G;+txPy&+Q3?*#GjX(Xmm0)`IDFpZh3sxDH)wO zWUZM>P8L?GX5*GMG|Xfjb%qwjI+R1DN0Pr|Bwk^ywQX7a@K{gS^42NH?)+segwEvY z1+m6u1Kmo7v0l0X5n;1GOnb;O5Ijy4>S*q?Ho1N~wz9;Ae`i3z0yp4Ku_x|5-vJb~ z{&H}qGmjNGi6K;z{W&imq)CaD9jrBJYVR+b<2>Fc2>}ef+f>{ZaZ&$wd9@z^osM3p zeT)D5hFOaq_6O|49o|C6^~@8qkxa(Q_UG{s~Ktz`^pRi){PNNP`O&p8Wr(F z>F&UCTxZY1e>?r7UJ{qyR&-Wbsxe*>wLK*p?Jl)&UZ`RANcc8sg?n!_2^9OD1+I{M z=umdG^gkY+8vk)#UkyJ^0?bpkH%SKYxh-bB;jBQQKCFn+^(n3n*;|oRih7O_bfLOE zmA2&45_KvGoo1r}>jG~ z7lWmi>t?_3_yB7OiIj=5;4gKL3uV$KPKR%k8m2kImseMHNr{+30ZY|V6tEYZ&Wv;I z+GBVt+HIr%hgF$iXi#^*lQ5h+i>4xb*EzE~$x4Tt`eK<+usz-bC&1?=_sQ(@gQwU- zTZEnjfBT^V-9eJ%01KbwX(n=Dkm9DSwdfKKK!#^I#KL}n5Zm&U^5$R3;WN-St1(I{4g*I{y0H4pkaUVXbrZzFT57Y|cd{0b^STopSf7h1h z?aJVQ`D0Z?dG?Y9nN2i%2pY*}pABbA-S`T4)#{c?NL${a^_<8WEiLNII}i#^-e-a1 zy=k|Fi-G$k^V#=(ykz#Gy@KrBk;z)S^;;xt@vDoXPvEQWlg$`t>Mzpfq-)j_hmzGjVh8qB-kn=BMhc{06f? zybPf;EOeLyUfZSa0ZeJ{ldF| zk!bdmeBkuc@f5o9{Dm&Y<(CWGfBBfoe_xC~sx^fMjA6x1uAvS790&^=C_E-y|J3(> ziw)v+18l!NjGs5;@hljL(t-=L7_MWld0F0|1I5k)9;HIYT5hNa!@5GaXrzJ)7^|j5 ziXZe*TZ{vvEA`qUllFePjx2=ppJWM2#t>xHi^`LRUfc2?F2{E5N$H`*hg* z=7pJyZ8td62~||&L`I6 z^(Zpm#rk~({eVrI9z}=eCX9wX&|@~x$9t())3mm6Og{&W@Rt|Te*+yNTWzC){ITsD z=xjX}Wq#cBqmTPEO{lhHT3RLN($g%cLeRl6HI^$AkL1%_B4MbLoxflO)LVGeY{Aj+ zzhq7l2;DVHkqELhDEZg+$`%i^Eqt|dQ9R6*LAOqqp|-iU zU9p K%%46Q%{$07iWtnpPe*rMO;~yGeG$=`s6;_&M{1vkwW!%M?}#hjwdsv^>+; zrDkKkm>Q`j_+VqvkwQ@(+zrXv*HPgSI)lI54kdbhdf9a4xX3Y+Z_QDR%pYUZi zr73bJZJy()tYCwP*yGWEJdPAgl`s+Ef6hxdTuuQmIJ{4y6yffuqIdw5bN2UBx?cnF z9O)_<+O&4Ij^()I zb8OEBNh(5)e?z)|`cOZvffP=q6A(<+sBay4tuS)VG3=&9Q17EO-`Y~Z1G25wo_Gxt zaM0cIZk#sxc8nISD5tmR!|Q$I-`?Y5{RmJrn}DQCnP2P1+NtY2+D?8@LZlnKiUAZ( zg9N2aYg@Z)MtL8?l8B0tBXZoRiA3F5aQTu6#StAse^qM-fO?gQ109iWrObsRdx@t2 zF;!J9G58P!eee10Xpwg6qw{h&uhA{#byvW6Q8WO|67kvC88HD#(J=obnqvY;b}Is8OQNX)W?9C^c#QN zdE4A?Z~AV$J?}sqjD*bb1sS=W&p&VYE{z#GWu{?1Eb(tT;W;B)a?&T;U;;yrRRkKm zpHF=TG*>#YZgD>6tJRg3xhe_DV0sZ3+o?g#e_-tRrKlzkNZSKK_WSC8@KL7#%4gf`scUBDDi4KPn003g!001hJ?-wnRNXvhZ%LL6> zsm;3f(`_ycW4f3eY+OaxO|*{pliQWX6n1bpVylQg#NSb*^HTeu3w;FEoll3Xq4S?t zWc-mwYveb&HvP6FJg+gIVOhlcgg1eJ>z+IYYaBag1^Ezwe~r&d`HpG&rm!ZDb*D3V zoz*Up^(JyqcWuQptdHYp@DhJL)r-prdVKK)^zcZx{kYAvJb~--HLDTHq&JgeP%x=4 zXs6~&(>bX{M2A>cuY6@Ns9$H3u=$k4}ARrxue#7`N$xt7^I~Yjgs#_#9F&x-5~u4e$iC8KhvuWLp0=*fFSpN znrq$3UHi|y@K->rfo*>t=fuP^MXHM)>q%38n~DeG)zpx=mQwt|j!QSFbqBgkDSo_* zM&#la?Pm+p1Zj)7wx**2}mLZOnk0+)e-jxIQ`|y^6$XG zI@Hn`6I;DezMmgXKhvHoUwAOCmkRsugIvz=Ok`O4!ZpoohlGE-*)3R#FcUq3jNvTz zdq{qfu$H-8^!wPIEa3Oqk-SI1rtdXhhXLPFx^WxLl?X6 zgA;HRhy+^n-6>)z1@Fj!{R+K6B$~14zG`((?w>(W+cEoyyTA&k&;eGD`z|#HNoex+ zN(PT;5_GvOLRL36H#F?cC3nQ`!)~qnZqz4BxC&YcjZuGcXD5C_SlnOBFCYLhByWG` zf)Bk~1IEe<{nQy76ek(4@uH|DgX2yHdcpXbLMP!kg+c1tcRRRT7v)R1G)uL%I&AfEQ>(g)L5Y zkbt*57O;P*B~siH-j63gJQp@Cabi&yER+YVzj@03i04fF8E%@VY+3orXtgI_o9EQ@ z6>6W=dyq_9Z%+RdrG7fND)BvA?z~B*9ea#)#gl)y;ug<#$A+3goluXQLjSermk0cj zJqGHRZ)XeJ3oKZvpjk{k3#z3y-0V$-)p0@e=>gmB4S%FpEbez1%2ivQAWN#@V zgh8;YUxXb80d^2rr-ln4j0H<@f2(P%ysKz{qdjNF?J0e-?wJo*PA}E9t&O98o^vo! z1BHL;U2j!&cV-5J_V}n}yerQTimJcwMfp9qIi*_zLqK^&Iy`3!wZNgpS|(pUn5`#R zx6faZ_$6**Ce#Ry{tBqN87L>8%qRFH%1Ng{qMla?XTV#;?*#(EEBY(F5!}^ z&|+5~9;BkcVJ81W?ujNJtbj0@%Wz)EU0;6~ita5Wxat6jp2+Z@=|Z=H1Y!9@eB|GS znTQ#97SZ0fk_*tjzDVG=+a9)%=K8!Q3u!7Y;q*~9$ z+YC9&&h;yJcLe^tyQb}$4DS#2o%+>Dm<)PlD1%Z9uYI2-o3ef0=_CEE48;X8Fzm@{3B|Qp*b47jq7eVAU{{&J;w*+dqM0OspRt_ECS=fX2-4~7d zOFP!ZouoJj4F|2RgK0aBt1-J>(LH~5nbp{MK#64^0z_(RLrip~twL-<$tUjgDn2hL zj;$5V_QULRH+Qi{GGx|Js7Sws-Ty59R_R2*``KN>7<6xM0SrT1)%rwOdFxclB;^@q z1Eu)6J*~T*aMd`pMs&6c;vm_-J8A)HB^P_{B1U+$IRjjsRyUCjO&!GwxPyNLhr{RJ ze9a-H;l1ue6xOzXKD}{A-MVgGl_u}e9J&O~{ z1`vc^2soCc6YnobDBJwj&Zi+#zXM^H3LU8u7%nB36I?)wmm~7pHN~ z%_ij0QMD~Qy%S|O;k0O~P+@xwZWEf?fy70<&sUa0wSRPL9t;ySdSWeJO6T@$Y%ral zu<$|=UHLr8*t72%+f(LM0K_B4s+6Kr^+n^XNq#v}c{#}_jTwoZ+_HR#)aDYN*dQK} z18%r21towHt|C=YE&hKXRY%xg=@$hfOr{&qMrBE)24C)Z$EI zJALI}x_-HTXX=~w_sSR4?&C!jv$EnFty}}Pyx5i}c<_T`_bdzpV0owSN$q8DVXGZ6 z1z+IgI~m1M zO2E!E5QOTrE%apvA^)oZunaJ@2%EBJ53(?zgWCNhq{b;^g1VS^q9THJ137TNK7k9X zga+{@vytccfIwiCp^8z^zJ*`6wh!oc=jVP>G3;l5a|ter(Giwh>0I7!lR^t1of90n zNaPm|%f^4GI}$|}lQmQb0FM*?|F-kEuJMklhdylZ5<3q2!B)YEgUb&thIUHpmwmr; zQ^fZC(qo8NwGlt?f<#>Ar0u_nzO_atAbyp}f zz*qpy-R`pY=A@`VaZ*Jhd`9uM2JD+(6)e#W_$zH0R`x^@g19S2#fI@v*nXgB^LTs( zNuz%V^={OKO4nAuv%wK+5C9RD714Wx$*-aoKpAnln>?j zYZS8B+{_!Zw;C-g9S!_wGOI!icv`fIp+|oNbT%Q^=wtp)V%<;!$as%$@AAC!ff*(7 zio}V%{%xJUBLS|2Ui>&9d;eb+fj3aMtIE?syFozd551*&cAXy2U2$LTzmWVbC&M81 zuS}2oS^KUWk}eYv%#!lB9s-u2*=I#V|Ds1b^uJv}RqXlKOfBbpOb*aAq?>Hwzm|X1 zlwoY$G{!vSd0{dq@fKW*erZXj=yHG&!Lh^(7p*qpdZv;!`P%>o%VA^898jCsp}XLZ z>La(1t@`OIZk!?}66$=!<*mPOxDz(K&NTYiTqIOzm>eHFOvg9NU?qq%YfHev(^p4o zGnv8BH{u9jA&BBl+M(8U5Sx1ptF?c!8e8Q$nEgVCIJNRw2X<5+O2mzh&(TaZ+YhZen zL3Z$JI7T*b02NnxTC+r9Wir2@XxC5uRUSDd3YwR?>!#vor-kG-y3kv5Xhx2KM~n1t zx)nKv6egKZ4s0HdD;{^F!h+VPnm}R21s&5r*Ngj~6id_-FefB&wUY1_8;2BtCR(ho z30U<-{Wz|6T$zX6ACOPqD+PZOwhc1&0pjcylBG@B3%~@^nin}Flr9XV!)5~P3&BOz zpY#U`F_O0#RYJ52d$dGu(#Yd(a<^3FpyYsR=@oh8R;;du0dKE{i~7ftcd?m-4NEZV zvZcW8iuaY&uTgg_n*^%rjvlLq?gqw0RCK20VH<@7ng>@+zwV3Y64-xbN*nW)H)(LQ zfXgz|6ee2sphMev$$jJB3V5=m7$Q9aLLC^R(9GZK#9FAu?2~$xhIAp8p!NWBA2N2V zSUw_Js-?#v5?@x61TT02j$^;GFQacW+-7O}Q9cCQPy6C7k-aK)IJbF%+2IQrkw5b2 z##Tsyp#Yt~s@>efl8b++8&c(TK^aO=Jieb{E_uP07SALcY0;mwt+-)s(u4^zErm9u zhrm;%6^lFzb+*dP-7-twmvCCLwO4G}_W{h1P1jZ+FymiMCeS0bLu>CSExA20)Y(Jh zVE=5YF&9UQX1)9BxW)WpT3%~|*f1r((XcDMq5G97=FtWlXYqgPwd*Fp`HvUb2;!Ph zOP7eG*Th-J?$X*vGjB&$x5R*ys;U3Oz^BqBa&$_1CSoHqW=1Sx7zf*9uC@-Av`8K|MQ6?GBd5pKT37^8*E&5R)-H_pMpcU{we_`R;k7C)JS>(i4f zm7b)rSt&HeFI=YgpIGBD-4@R|vx4q+n{-)7O+L4~t4lQOntItY#Z%s3?=@`l3bt|r zZ3Pa&kAmMHf-79 zU6k5Sp?31Y$*Bi!5e6=1`EJQ)pA%UbvH<(jImvZeOBcOG_un;N2;qb`iE7goMb>xpnVF?Z9+Yiw6M&e%`#EyB^h`09;WU78{kh zb25K2Syr(&q`VMz227*AX@KL7#%4gl6p%2w~I>^u+`008zj0g)N|f8O|E-%c}#W4qu7 z10^k?t;^K7n5a4kS54B&-(l(uN;0ef?D-zp4Z`8sFE`5?m2un4BOgr>3D=!@L*u6< zG;`SxeVj|xEfnER3Ms_Sxy>c5i+1J13Ek;TrFYekNJ3b=R}-8eW5}jDF#ib6PnVOK zYg+^2X2sxta1Ei`f0&=df03e@Ad34mKXV{_wyXZ@urqQTod;JjVS{0D%N8E-N{96ni=zk{=ZmR49x zTFt|>2oDi=v@(rZgMr;A#9V`5rVm;4^}+I)>9)N`o8^M+zD;=$Cn<~A;a*!7k^uF79RJQj@w~tT#-bVS8h@s{Kx`yS-f292W)-H#OI3@JiW4o!g2CV1l z#h29pZxshy^x=D%Ltk0%e1aT6{Y|#1@$AtJBVXg^H~BmIiQ!TSDBo!|i<29c05g2< zfcm#l(ea-Z{K|!~P zcs&7g+J;BJmZ)jV_HA(MtLQ(Y;QD=?QQ7b|f9pAG7{ldDT?>z>_z==Rhzz?|h^P`8 z#tER5JRqZlJD7E(8{i&%`Q$&Od{Ox-ulxr$fi%>r(Qj5wSArF0vhF{}T{CFosA?`WsV zf5YZnNqrOnKL0C-H%(wiI5;R!qT026N=ow9=Wom(eW+DY<;nTIp|nu*cFYTIbO~;# z3}4Kh*X+sCA^V%k^KXIXNg+KCBNSWWr!*t6Po5*7PUw0{9Wsr6A*@=+_HuXtZn7!q z7c%j2Xt>0!pPK8Wd3J&h9{{O zRl4w-Kue_5*PH&}DpxX@&%jW`aB^C6G>=5TsiH;2oH*t26=G&b5wFh13&m`_i68FN zAFl3ztFr(h@T}dP8|?y7cqE?+f1d%b3)$P}1xkAqonHtuL=4pN@vECnjS}P@enH$x z!oR}me~(~W?LKcCUXG-ww+!D;-~8M`WoxA|AGI@FoM1wt5wsb-3-4-8Es6NzJ_Vf( zWLO)$#jv@&yrYnP)t~C~-e{{H!0V%9= ztWKwba7zNPhcTBhU?Fe0s+6fBREw4=ZWKwv>1m=(VbOtLU5}bSs(-!gkad{Vn$e1s z&WXwG)UqZBGQ?momcvN2yPDU7JZMydLuo|WkVnIR39-pYd)#4VWYWgJ#ATORx%fb8ora=-GsePuPK35%TH5%Me;-iqo_K9N?QkQg ziXtz773>P5{b;R=?qQJ?Z8#Y;TvYxb0n_8B7C_sKEFzQ77fqVlPH$>}u#}qarJ+JG za-E$L26J1P(Yqp&m}kz?j96sc=i{xZNo_;DP{i#*KADG7ayfuVx>3-SPq*vF+b+M% zsa-UerQy&y)Zm~Jf3nCAP5#}`tjCh{=D%fF!IoK1(wDi^``N}9T43Li2ytAT2brnp zm+mVCrT$vSMnS*S?+;MR1-sH|C=vKA^*_r$8#EwsCAxcTTK?Wh2!?~ki}cJ-@-(56RW^?O-E0JXIIimC0rNL%o4+bnYu}XHhh?-o^ zT$!7=nAQn0fA^E4GyfL=r3!xJj6dPK4%OQBvLbkO$5L~^71v9VGdPjyF494BAOrr` zZE0RwU=j^*at#M;6(|O~I)dyua6Z#>d=u2!y~4|F`_lZ~epx8}6K{gr|Lg6n3prVn z;9bDqMlsf~hb^#U9E;gl5H-RccJBj9KY@A$LTu`#f7@bI9C7ejuU@y=Y8Q`ESt^iu z*OErMIl5h28CgqVJLI6ai$VvO+Fan1)Rf8(K?;;tJ$ytQ1Qy~;obr?-CdoaT2=`Q|IpY8r#;dLLm-ye& zp_Jp%5Byjglse(y9t35}0Y;O?BrPRki#-h2bF4PoZL`Voe#!3wv5J3|)2-X269C{vV zc9uw3e>z48gKoruJR8-AnTA!VvLqhDe_VF3`t+#zzjgwrqvWL^-<>9O_-qRqMTc?N zaZRuKbRpXL3zGQx0dCNs4>Pg1XFdm510XgpEjcYCL&#^-E@g>9eZJ1YS1`L;z&U};;HZ2={dyoZy@-T{cZE~t#jL``TKCVfrUI8d!yK66`uHAZvT(ZHdgY}9f6w5s z``GLc8S@r(pm;(Ro%ay|iY+O<&zX2306}{pMa4UWW`uxfpc%y}xLpr7*qvYpph;*y zM8Sw)nwx|-9!r%2pl994jbS{yw?RMeQ=Ihk-pxpMB4oCG+sdoMK?LN>< zljrN$D;88^!6tc8Iy%6|H|7g5A;Tc`&j7=C7*5_*^0S=5kJ-u%vtnf6-{fq`NO~Fm zkYV=!OVG+n?i^T0(V0(b?AA>~4_S(n1S54%f8~zWExHH_3$uEDBN`Mxf4OjqVnnjV zOdql#EDhCBY?LK8H&0YNsTd3tk9KI=0|sq{?znF3okI?M%F~XDt6KX~zoOgSK$!Gx zit0Awuo+_LuLVh|mLf^p;D#32PcjDOv4hKH05ZiYuVCIxfFT=(j1B{s(WNTt!141| zJ2k)Wa2QPM?Mix#&+GrY8_8X>RfXDxamJyJw1xAGM7weU?RrfyaUWM=`M`KgAn zbrvg++Vw#aDrrrF7LO(+VLW1w{6Qpb^D(KK@N8qd{N~SJ@edG(e|MSKQikKx!$!yy zWI^*>zS09lPDHkq_7kdN@Dg#+vXv#jVK6QRyS*a)8r0;xja3*6H@=`Yrd$a&>xZ#* ziARa5$rL|neU!d{1bk1oC%M!JJbzamQ5L(jpR!epBK<#zGNQtY7lPvnU8VFA<*~?V zDE)?s92}Mk^lHzAe?dQi)$vy^6+t^!h#{eb(DJ$fzM;6>WW?pKBh1Uwr2?eNo%I4w zzPobQ0Z;=aPMwV0cVPu8VE)X5qsBD>&PN;xEL4%$L9hiqS4`LEa$!wVOks;bv~%tO zPm3&cdRLC+LY?fPVucI_^CF_~!xl zx)5L%NOAqZFd_Hf%Xi0Z2=uzUMrr1FN(I{07K;3fo2*;#JVxb{2RC@3xJ6|1slTpb z)d#v}3urZ0%#!OG#<86B1N5|zX2ya()Q4LTWn=o?>sF&! z<5u}ic=~n8KtQBG{#z>e;|wVaJj;lfQHwK$06Hxc1{yhK@4r6`P@!8`(^X_AnB1m6@;Tbz*qro`e`3>n`PTkDUV{wBS?vWt*!&y* z5>`s^e>{JK{0IS=1p$iH=)xhM1YCDW*!|k{`n6nJY)rn`h0uD{+n!NGg zQ$>|2o=6-`hc*jzr-+U;y7lm7ZBx&Ax%ZH=b6P?bweLp89D?SGsQ*|7`a`zG?7Y!e z7P(>if38h&zPM{#>ISLLYxs%oxD;J;PRI=r0wbB3t#(UtO!d-%fQYN>s~sn+UH;?c z9I2&W2(d44&52?Eaht}re$)jD$E27&td#eFVL=T|obY0yb9vG@-W-sAdCJz9H`xmd zHp9_KZu!K#l)mpT%J$uSqZ+rh9$KdiN%&(+!| zOEujgWzW&HkxsH2ws@MhsQxAZuzB4VvjAEJQU&*hqq^e28LwconzWgD^oBi3)uC*A ze__z()w<5vNlnbyXoW~v@sVtuX$md+w!e>UYCvqamTwt6S;8SGd;E9;OPY7)TX8g&P)DtE#LFnWd+hF&$Te&u9@j{T7)pL%s2Jsb6f1rK$ zuI)@IjJUeRg35@-7}o*UhN>!g4X%Sp(uv)DT0{o>lk=E$|ktniZwzZ)Ew=UXGUQFH732>KI|>my6CO9i`0i7?Z|CnKkN)SpV1 z_XLj;4pL@_l9AYUTAEvZd(K5oq~FnNORy>p@=2OT-hM8j&_h~o(%7*fe^kxL?I!HE z{dxyl!CG6e7UHqujfhc9;|wgSAOgQl;{!e<^Qo?lilqD}R@AQ0;pFuCjpZA0!08bI z&lsH0F=Yo%Y5HdA*Mc}1_91u8G|BZxGT&zf#_DV$gV`-Dr!gORG0@3BxNRgB#Zg+y zofT(1fq^0r-h7-{>;wYjf1crhrt2tSkEB@ex;dR&W1elq(@_QB2zHypQZLf8dEQ5K zOl`xgh)#+AUBiO%dCL&-XJ=?o;X5*m8q&wi=`LTQTQbsJFvrp?3ub}=l2d~Qai^aO&uM6j1Yz3x1e>=p8X$b{G@C4Q=~#Mc^;6!VLw+p8Tg{|- z!Q>nGB?CDy820Yd8BOisUX&y6^(Xwr>4V^V`I||4uK~C^Zn{wTwOA20aKznB(kPTD zRIg{TUIOL-k2XJtf0EfC)Tsyq+7kz~Ou!8dVfT4AX`ET;6_5pz<6*@a|a`m?aHRN$2$h7E}e}C`z<1NvB9hmOvYWw35 zk_l7+rV7{T$@1k0hA$I}{gpGPOj4G4?F~&#Cs@0LF(J_MbY^K@^4l@wck-1|aSi3StI$wm6qe~&N#(=42R zF6&MNFBt~n0cFBHn1Trr9Z|-+t&`c>-Cvq@(Wom`e@J0ij#uX{N6j52@Qn(s>=+DM z{#0lmT?;2>-io^Qg6T}%jSc+A_4UFV`9^64HZFWtFK40^bOhcDg&nJnnU#j)C30OZ zN@Uth#vUaHHr4|vvq0v2k;EkA&WsGis8|c2R3+s>Xk5QKahf%(z-IJ~Foz4_A=)EG zfay$Be_kbVEY`?>I38d&;`YOqNddyWjuoo={5x0zU&k00ICG0M<^*3|8-}>^u+`008zj0h5*zO$JyO00000 DI5@KL7#%4gf`scUD%I2Y)jX0036o0FfE{e=NTn(uc9;k@*HO zv{D&ILx_Y>WWQgYc+nt95XCr4DWq?<;Wy7&_Y{-i ziiO}aMJ7O-{j_h%e?hvp@Si1K5TBpd;S~~vITwQmtMS1dnIuNED(l?ibK-xYi*y)| zP+Fe@Jq9Xh*x|!7+FHl;YGi-`pQNNJ$@9~VC*!0Y()Kx+ZvfjF8x8$a`?a>ccntO? zd^I}Earfk=E*gU4N*wYj+V}X%y0)WOgl8IrZJTN0g0OMYe@hl9yOWv{|9j(jy{<+p zp+Me{^-{29x9q^O@oeECcuVwH{t(m4xOyhUPag$yPCF1<$PQJ&eCT}|o8V`OSsT)v zNr0Rn;J?5X6Q0z7h%}CSibdzluZOd5(oXFjIf;2aLQR20&o$m|z?57mRHZ&Jz-d*V zzWj@5w(bE~f0J;R?dYAu&I7W8z8b-YB!oX;zZRy?Z7+)7G~0rH1;_UPvx|cz@NhJb zV`{;Qzp_g0VVw(tM2h-&0A>NVP*YE&iMCJeRH(4=9iq7z)Bu64HnBn~(7&|!({Yu? z2TG?To={bXPz%bO2RQ?DLWAZeEKhMgdGhumomb#KtHW-@^G#n27+ufA>pW>au(04`l=SGcHC=gd3Jjl01+_s$|$$3R< z?gZ{Jd#(`zREho>*3CLl-V@vO%QP*63xGMO?<_^}+diN*WoF0xVTl>W97z*hZuZN# zlza##e<}cxGe08auFA9z+2e+?TG{{_%qT5d7Ih~$&N}_Ubonh=Uz@lT1!DjCCDX-B z%wnDfJ-n_j5J{WQD1fzCxv9yumF~Y+G}BI&8HjofBrjhb!1*tsW7&s63@mL$Dny>~Bhlo@>zdYb|sD~TdY9blw5fJesqS{y>ojZ(6F80!aOQRCE(qLEEAHee^>5gxT%WI~l2;k=f8iV7fHGt7G|w>+i%Ub=n#*z|KS{Pd7HxS>)7S}Z zPUIQV68?R&ApQ}CbmMqi@2)7D?ERg^x88!5IJ#H@c9>-fO45?uh8^h$kRYbRHfWX_LFD32)F~M{nsPx_TTJe#@HB<0N3CCe-W|zkKUoD zXNLF9m$nIuEDk9lcF=xyxV@!*zab0Jl5|V$pW@kmg5PF1->W{$eo1nVwGaReh54J6 zF+;L0U^cG?c>LhLjvIe35_v)4|1PkcIEZ{68d4rz{dmRO+K@HmB|0WvHf zu=pwIK*06&=frXV9B! z(Pk1Yn1R~S?8;ajZ{;s&jUYRdx%Z0=zs~2tuPw9QQiGlv`loO|plK2R4kCKXU|Q$e zeF@C=Fqd7$5<(cye~eN%`UQ)#%*yGPKzQ{fPIvfHYsYyYpECVOC#Gdb%h7SwhAKT< zpx7~IGxrk?mp>rOO!w_WbBY@0z8$&0W(G6TL^R_7gjia#v>A)nccHZTv@&imb^Dp~ zSSm4CGVP0z;xYNa_eGP+un(u&whY7zfQ8d?!R z(`@D1<5oM5jW;#zeTD2%nhb&MQC}}RgKWAzdi_u*p7Rs^qfJNBn6kklFJGicIwnUx#hy{YLOZDz{f0SyKZCIdH01;;( z2ljt#fBB*a_-UnG+qvW!@@2tLzOk`#;f3B``_#4#8Lz=A|}j?MO%%;n--Fe@&6SqotD$23IHLnW^_u27f)?_vL(U zNHlTLy(~V+{Pe+yj$JO5iPr&LbSb1yY?p`%#)mSu12G)NLyQ$xJ$~UM>8Q~{3gJ1VkNVK@hi z#qbOqB>BDXnbG4{LKf0ice+Chv z#&`x5ftp`@sDpD5P}@-x3nVrau-RLg`dUXh*sG;@m_{-%+nyjKW>M;ugRxmT8#E*) zSVit;1;(Bh-0JrrkK`3aLTMZ?d*=Z~|7tBGiI@fpxl$4)AC}igX*w>PozjNk-Eq8# zB%0B%4rMkJlk!Pwy}OGe&UtZTe;vQMp5L;OCM=$X==Ua5vKz6C`x@ucq5p<~G0-cM zxKol)`^-%{ei*U)@6L==2Tt>3<0s`SG#BM`)S`}O!5VZv`@Mp~36_M`r;g!1U1$B1 z>AqsslHWX5C--juzeXtMvr!K&OLj(DnNv*`ejs0sQ@~ClP#>w&RYG<7fBzdMp*wB# zo(8JX6^S{`>}n4Y1r{{>P#R|1ZFrK{YWblIVo8_`%xC4w9iWyHAwj>L)zX`)hC-yA z+BD>ic~6NIy1R*PmLK>g0a%z3LLSK3LM=fY9&irU#Ab`=^LWk?Dp1M&v$YkqQ#|0> z&Lf7+S}`Vdfnb1vT1IAqe>k77>Zf(%g+zRr%;F>I$uMz9xBHwX;_>LCwtN-Tk65G0 zX>ubN*~!3C5#k2>3tcmWEH0SF}@OArBxgAA+ zGNcS~CkL3VlZ1h3Gj|96tD?n*iMT*3;R}4eLNi~f0piNQE3v@$@=cQ7nU>{SAx|P~ zLg#t!cLP+`1l4taf5?<$1zY>pT(52vs4Z)=KiWckvNBBQZ2eho)1|P-k)d38Ojv-ez}RS_ za&R5@;ikJW3~H})^l_rJRisOJjzVr&RuIU9HuY?uvbLKx)v$ zKw;yAoYtF$f1TQEhzF`2s4aR5h3i3-9XMcBn2K*KOBlmPabzM14y=D%L*CXUoPUmsEAJj7` zUfUi~NWr{AEj^ncq7F{GsZ!Q1aqaN%I;sAgQ?K{GJRt8mIx^?f4E;c^BHZfJ;AT~YX>U@$mG6UJ zU?8?Z@Z6fO-7oxJ$i47Vut(y9jdAJO@ERRC^WHsy4~)I*x}X|S>k~;6yQyJVZV`1w zz@MNS>acXMBL<9_XSl&|0yIT?U)uEKa+O)Be>}h4Fe?xg?+w8cw$Mpeyb`5uW0GLD zX&vkqoz#Ss!xCx|K&X)Tp&V)(y;J2K7US{Me@Y{?X=2#636(}v&hxy5kO_k|xjG8_ zkYo@_EMC+1&~~fim71*&@ySO<9`J`I703Q{cxA10=bO?Rf+li#=EIhmkbh_eS^Qh5 zf1W6+(uxGo<3Go$W58;NlLmRAP>oQXcryzMfIawM&aY#dHc`7_&V0makB(|}66Vg! z&x#|NQ}st)LIDRmfX1G@EOykv_^K?gkcJ!9x;0HN)nhXa;*SLD3?x(;`ZiFjYZG_V z9ztFk=x~dmiFSS!lcUe8%R~mOkRbvDfB6r5uSeUmOb@CaZTgZw5P25)W$pZ@UP2aZ zp++5(|GL0<~< z1kodam?0`v3_)_9%Zm@YRP*>RhHp8>FBDp#T>if0pE?+RF&IZbw?;G$HA_D3B>~vS w2|NGkI?t3)O928u13v%)01f~}jdxa7m@KL7#%4glFs%2qEtGuECK002QI0g)N{f2KAY1XFNK;dWOR z$(a}=K^Qtk#((#8;IM5MWQ)8zi^35DfSN^dc|T$w!t@PKnpzAZDTUPlOL>4keDu$P z60gs9Y+shqDGUs686pvl_D-LBEV>sE9-kNC@XlLk8DPNXq`t=dKuPp@kCB&k0prr~ z{*Rp*TBJCrGJuFz3W$c8yXCnTYFgDp=3e_cE4$9S68>2Lk@JovO= zSXaHNCx}j>jjyf)EfrUn1DXK5EIL)~Z)6);gj5Vz+yjl|RrHp)d3t5?r6fqawJ~lu zi9CsVGL$X)#FMBdO%|`IKIRlL zi^wcSRH@2;f2?}|unW(hR2KV{HM$&6*-s7g)mWCgr$^Mj1lY?YGQfkx3piqUnSRe>~NU}*WGIEJ)1h$`~s5In{w%=V$r0uG!>BFD7VwYD?I{NM7J>kMuB z=7JM=o!vZva66HF8AvJi0!6s-X!KrPD=HCcs1pA7n`0lN=!Ph``)$UsUEVlCo0U&m ztO>J?o@<$2ZMkbH;uB`j zBJ!yZ+aM;-Sun4KbVC{6XbDwsUs#g(e^x?p6b66e4%ohLUu}0tEDqJt%b1cn#Fpi> zlw{J#$FF|$=)SB$vsx9X9B9)|la&~67nNC=A9O-6QWp~_p*2?lxBP}+xFnk#xQ07x zu&@?ygRy&lM_2{TWIu0Gl^u>d;PiHi^UAM%4IxT;%c#;Vi!UWhxpi1B!oE8{f6Fr? zR!Sq4z6z&vG*JzBt5}7X=X+wYzXF4H5hCsbT4B_04_VrQg6sGT05WX)jdJF_jtywE z9%^H<<6T~}aOV|j(M2oaWJzp+TYvoy%~L(70LBxK$mL8t$d*E6vJ7OYRYGuh4z>rR zuU}kk&_Thl&0UtKwh#Ur5!UA!f9#%Y&=ajYSXb(WXmxIgA_}8btHzktOgLhIcH?D^ z&`tQiWPa=9QyY=rKm!G2-ZKq;53_9%iz@{xThD|HD}_B_<(oBZo8cmE*ss*j=^cx+ zhrl#e&@f8v%?%P1W+7H;U9gBd*&NdDHG2tmr8{~N{6^*H_G1{nJA@V;f1t=8~#kDkBZArj!RuY=kzONKQl*y^t103mOIa= z;)TE|Y4%NG*!53$umWJsVxUNG)jU*AgbKTbvGME%x&bSa^UDnH`{`LeA>~xYS!K>YM z(Fh>0f*pen3&kdm-pbu1)F-UwPi>YYuaBlvQiAsYScr2)43KUBaI89w`}L+EdtXr@ z*k{-;SJeq^t*QUxt+=Kc4)hkRd6+vh4_;qT&wN?>4MwJ$b&%GwfBtDZEw^*&Z4~hb z717NXYd(xkA?+&69Q0%89fvtn-yGL4HHWSze`&bCOX1c)Ud_n<5;^jBDjKNxsG<+a-mK+qMuN0moXk%YMb=0T z`E9^1X`<7u)(Nr#$2G#RfhPCc_4RNz?lu_p@C1hG>i{&57xK@l6nh#dXTi>d;Z>4m zO|F6x)2+~N<2e9rdoQ z8FzMnbuuQPHBR)T9B4UtGT;gtfsrt!hPQz(GUo`54Av+gJj^5VxX?B%hs<$6UCins z4@}TFm-5C7__!65(!DMSwNXoJNrY_L;dsB-WCLrv-buU?sEUI#^1?(Y@y?*}2gNW6 zm}t4D&u1lBf3L4pDLg2)GVTrT&RP&TE3nyOj9+~v?T;fY9de0HTwtGnV677XXh3{I zGb;%uoYK-n;$HKOD;U=B5(h9?iJSrhC}=2!4utS2ZmfeWl5Lk$5no{O6yB}A*cDj; ze;p0`-(YaXG)}*1trpg^R@n=$V}aba5A)K*<%;pNfAXar?$cP#ZIo7QG?r`7@`i)$ z`vfEoOEV@Tn1I6dPTXq9$x8j1+n<{d>Gux=s>Nqo7ej{D&=usT!B7m*a+DcWOtlk# zPifc|Waq=3J3MuvBAVOa#g!Oow~(fG4klB$pK&-yhgo|sS+&jMqQn&@$mcW0o@mfN zXBY4oe+kv1`=ni|gp!8}zQ*mElYZ+bZT@9&Nb1>_Rq&|@Q*YKh>eNNEckYzk@f83e zflnr?A?4eLt@#3On*q%l(HEn*!c>%as1CPe-&X1Vrw3ym=QhBWSkcRZ>OZ4Smpna?U|cq zB|3eaAmVY((^*vJ%&&~&FG88N;OCfl0hUmhXraD41nqdC?6nJ8kFh}k+28L%Y=N0F zW3xWQJ=Y5xO#?N|b1}fbs*^wlTQ4XsYOu=99S-lO%-V`MpmWq~fg^hb?-*NJf4o!f zze3^Cv1hJe4f#OGDa|-V_WA8ESf1JWU29W+XCf9zBYo^v~)kuPmrf8`jzb)Pu?u@q4hwSOwH8g5sSNHK}Z=f+x!A!Z$ z#s_~)FapblR2=FeIjxwZ2?tT*3+_3#Cf2PCC`+%du z!%a7Xf0!Bi2s_6QQbrHE z?}>o2|3GP&oxcQO^4-*|g00r%&NtDN{uU{>*{|>&zmmaxUR>tj+8ry-phBq=X48z1 zp_goy1QPJp2|UiNB#UhXkk(rj2|)_ z&AlX8H};VW_hHS$SdO?dN`%95jWH-k zSUMx>C&AB-yt{Qppquzmb9bE9^vWR^rf8XUb7TC}hgdp^kDUz5-kUk|070iX#^JL6 zxhEWphO>E|B^_-GX&aZ;n6Xih4m1)57q$dy>vY4~cKe6Ae_u4L&e27|UPfoelx+<+ zNoiY8jZo#7cS^V}N%E}}3kKaDxmor_4LH<7`jd1>ueLj@LvmYFB(0wsk7VtLg`jCI zMap99r9dTWKP#CDR_^>R+EI7u5;aNR)lnkvwW#|jB*yUmJA!fK0$20PHB6s?{QtA$ z@R_bS_xUmse}_BML{eC)Cp;l~mw=fa)Tw@u>`z}`vRmmjl0`BKR;}`TF5%A^wCLMm z+?aAoSy)2xivF=8ETt7r#$;}hS2uPF-w!)1J~jt)w!7skr~Sh<0W@Du&D4Xl0OCcl z^Fu)mmZDktQR+j5vXx`?;}(8roCjafT*gVSj{f&@e=_iP8tvk_T#@zNE6e}75D|$( zg4+l!ebJ+?0_#d5iKJ+=qg+H&@>H9#ox0iyz*A>sQ5>0`yih*a&hv;kYIgD$^JF(q zM<{sXNl9P>aPD?FPF{H7Qmh{9%H&FT`>|Q$N)Z%Lfnz=sf{}7>(Nks^ zG>IMiFjVqe5P<3V5QVaWHorpy&;CPwGm2mze{`a```pz}N}u|JT7TiTjwwvh8S0_+ zAjzF0#1dsTU?ZLHscp5cTqslnksU9nbyc^cfhopS+A$SQ%6+w#9CT^g@PuWr7VJ?3 zL+BLCO>3=_1bikF4z!&P*s|2@VC?%6ckV)JKFW?2yc+lZ|=D)54QC2ZMMk!8{?|;#;}a%}A$pf89!y z!H`R+3A@PHxIEIN zcEB(>Zv%+r&QOA3)j1n{r0qqV=as*PxjC95B`4kAPGYkr1PA4iHizf z9DE~))G}51@oVPQyp((LFDGXLU? zjOTo=^1&YY?c_1lHr~a{0Jgl%lvjX1xY<&kg9&L6m`qC0h7w#uWD`c>jm(4L6cvS# z6?W(}CJ{!8q@EzGs-c%6YU>p#e~UJxsZ11(*x@#`NGd+}@vA*j4A4+Sq{>5mMu5Oh zOaOY1xBGw!tcaHhkVp^?Fn+HQAJRa3eLrw=eWBc82zTP)`9F}<3oQ-AhLymSvcYXcF zR@Eq|x_YVoOy{sQf0^Z?X*soULlP?Hs#4`?Ox}uuP?MsKQ%>Pb67VKEBLXUqR_FoI z2B1*p!yJzy80;|Ca%E`12nzmJc`f<|GETk^fZz=bQwyqEqZmgKMuI9yhAC~Od~HH@ z(G`rXiM}Co9yGOMDKaDh&&Y*CX(zSJ(b0mYq^#sa9bi{Kf2n30w>g@OxA1Wyx^sVY zrmakFv$&Q%!Hn;U)odt%1cB%)V5#Wk`DoVFN*TIW4-^P$yMo1R}R--e_`fFScB?`v9 z?3!TmrrR@R_VyXhi9_Wwh0T4ZklnRD|%s`9v)Iguy{*9UVeYHQ=jm;=6 z7x85NJ2iC2Dk$6vc$QX;8w7DLOwWR>&6CQnN_|=7e_kK~UqIeW7bfFp(V;z04&(?F zk0tM{xB>}Z`L-9?Q_)zW00j4Hi~q5QV6LK*&e1GbTM(f71-Qm;t;*`qT|asx^yk>gkcW3Q7_O9N-S)HSUl8@$+;{2 te2c|U8cP8JKLbAi0ssyG*-pw@KL7#%4gf`scUJKpK@iUo005BC001hJ5f?3yNXvhtTGDsH zxdJ0i(wBQyb~soVgMaLu&#Azh_2b^=9De}M(FG6T&o3AR1M5w+O8`THnhQ}nn?=bR zb7p2Z`5j3K6`EFi3QaNiJArNnGKoIVt@0-AjNfL~4x|=$sS=Wg9OUzN`xm$pdF@A9pK_IK);k z*shs?bYfetZIyVF+>0>w+jaoR0A%S24^BvC*6Nq`?~Eaw7bho zOpasEgkH)kqs+Zi$~1DhZrIyO#X_mEhK;?Z=j=3f-s?8%AuETk4Z8#AkrAebKy%X?y3x)o)=eK+I! zA*|ZfxKL%Z65c_Do5q@}v=LCt`jNZhywPc?w*ud7=9+_i{Ep%$*O{F;M z<34gRadUYp zZyYE?)sR$xB-rlaucDO9{eeK15G_{hda*blF$Nh+IZYp$e*-=b=7o)T{grtrwGYvS z4KlJ~+r;-Ull~z1rBT7LJFF@ff;=oF(@`N}n0y@mH|{j~&rpYuhwKyl`R=%;KBkZS z-%_N!x><&XsK9@&KJ*7~3CXml`ek+rC^Vd0Za40W{M9gL1`Nnx`r^^K!fW}{Yb^Ur zaZO+P@wr)4?2@3Lo@|J*l6y;W^dKIj_DmtYCnhX*#a+>6_uWT)M3xn=YVo&V#??7# zOBdI*0Gh&p>ykZuo1rA$guC#61D2~nwN?uWYb_QN?hm8p>lD-m3ie634%8-G$_HG-oG}92+M(XKh~INiq`ug?@rr zw%gPzmdBL^hj@Z!)MZNb3|*R&KkqYGejXFll`th`D}dHhAN2_i-{rGW_py75hQ z74}NooS4(wVFVIZLWH=&)1}*~^>S_JSV`9o1#5qRxfk4a)D+MLX(pj$_Vu8C^TXic zA}&$t&Y53L6$jhmO_8PJ{ z3}6Ftnm;xkB3|AA>I26$-9Cjs=&zVLo0m z=X;fyDt`D-PdrnO1~WJhqOsDx4viC$UB^FCMWg0z_KmYK zq3_`H3v*&dImO;(b>5}W>G}4ksHNLcw;^7=z^&U9n9E~M*aM^K&58bYt&d##_AY-5 z8`@ZZVJ~AP#?3KnE**M#Dv{`Kd<&=bnJ)8=sW3?eJ}A(UAuv(2TpV&LHF!?GS{=qSQH1oD zP6OPiGymYYf(Ua%T_{$#rl6xUQ22imx~~GZV3z%QL%c6*2EFGn9U{KLVE-ZETWckL8K1KJkx*rh;)B@OCZy}n2`i5T&N^R8pi?0K z$nm&+;1XRGD=#r}Nu)jp;eEM@Ux-OjD-1?Zp+d;gtNyOlE?c!N0&TAJ5u4tk%{coAk$p|E5%>cR<}mANWF=aORG?QWLI zAaVUhsV8wMPyts#j}~Lzd9npZ1?B@D0Mm)_Qd6;u$D%X-O{&3o^&bgK=2qdP z=1R_K-`#Gf;+J#=+k?cmHD!O+OXY{6={8ZedPif98q_vr=B}A&en%dM+|K>zzdhtp z-Z}90+{*ypVP@GV)%axmXgB3i#y)J#hXA4r=duRCB08^iA+RpOqQeayxb7hZd8e;_ z%Rsx?d|5a0O=_hO%b%6thD@ue1r!GS68;4Ot>&644F_?zsfo1pc9DOn^-BuRy|-vZ z3kbcj+r{48Z@ryN3tKrw24N{DmXpe5#kZa<9lwiJcj}OOUriKob=XMw6dL`{vytkX z0AC-4PRC1KZsStygetxJ%kBf z#ya-?s4i29%aI=L0DXU!pl2c(B>c4fUzq!R=!58~D*~El8K>Ir#bl*OE7Gym3x+6E zwQ%L5roXr!g=Ts>p}!iEsT%4~=6Q;*0s`)AE;4|Iu&oD(6;EJhRK(89$#!CKknBaH zn=I~(&7mE%ufa4+MmuY<8R>_sV=+{j9zFNq(rT#G*5#gqbt*ieH1V z@L0U#?zj?`X|9t`h5-XC7B`z+I-=o|6Hs|(jxKTTdkpsKKhMAM=KO3`z3Yi?nn zjetdEMFE&s*|P&1Qsw(`AcM(>E#I@pyQ{~&7&m`3ux1M3B^d!9k?pMCx?rk2P#Hq6F6LvuTzyD$IS;%`tg_Q8eNWt%!EeBFvc)v-)T zGR}Ktum@~mOK7{(}D@h(?tFa&~t z=RSXc;J^1zaJdWqx;qCjRu%U84zAQcjYZ}Y^FTw+_6cDa65@KBNf1^@fx?Pvs38&j z1$N@=#t4H#NM*UPLq4Kdb(;Sp4pP@eUV6O6yCgU+D?tvk0CI+SdscbQ-F|U7y={D#wOjlc-Hj1U*|qV z`+rYW2%HeUXW`WXBQPs!LPi{N=_k0&h!N?=BVGsJi2Q$4)NH%%YmKQx7Wp$AIHvQd z=-1~e><{D8D;BfJ0M?<@>ss5u)!sTgo8CWII``{jb}SDs69f?3bwrHPs%xaJ6)b-* z7F6grHEk-p;!lz!NenCKhzGHCAuBr^z2Kkb`+VGII%Cdx7XRSK$Dj4=Z`dYP_Y)XV zaGAk3JgNnJRWrzFGb`P~GTOwxea)9W?(8#=?wjWbW(PM2yQL457q9!8)5FHe#mU}tENY*yp?>74-R?6w+9s_dbcPzZO;_26UKt3;46q~2&cprZueGfS0 zTy(r(SAPq<aufPlE#GxDlU(;;ZAm2v?^Jx==d~ zKxVKum9sL6KIFSJhH-gJJZoXr6{&$*bU8mSca;)p#KBp=98% z4=~`2mVzTM9UYxsi<@6^Pv(deec!(hdm2ugn7gcJpMjYB4f-RHEH6t8R)?a;I5^M1 zhOF;{(2gwlNv)H|`SyOrV@vHLnWYvGQ(dsY+O_Ox6aCXEK>l&FAuiO?x46#AMp(c? z#8`WY0s+!zv>RPi!=Ha3eg7t~P+$u-=Mwvew0kgS=iTeQ$FjPG4FzZ9PONIZRyP2_ z`c=?cK8{9(cczRajZ&LDuE%o24~&j*OBN8^vMTS4U8jMJ19+lH)xM`jAfDS+ikl+^N$RF2yYN}mO;4JUO6EDSw3oPE3f20da^Lh}ISmRzA*mWquY zUIpz)ju26@pt*l%7lGYurYMYOjS2PDy>{~s3mjM%DN>^&D1>&5pvPOmXxoFvVGfSuz2 zewrj>%1SG(S7g&uEX3n*lb_PT;eGUTmi>gkxfBP2dT)P(A?f+=iHa#Rp1>E_fOrOb zg8xLJRoQ!~+xV+7`B^D(+dTTY5@ZU>b~yeRx&OQ3sln7|_S_Qh!LXWMfLYr*bAwzY z;ye;tAFi^Jb8p9rCLf6!@TvAp|FpPkBDX)}H#Kw1XPX)g=W|5KBhuCi<|U7>o| zCV`r{O$L9Eu!1?dB1Z^ZL3aIfhnP^@yS%#yrhL?zFIrvY1S}#uF$D?@6HJAz#71(G z_LC@BaD|(AsO#E@6rcg@(0zU&k00ICG07Z>=R`DJ|5YG_+0FclClK~e^1{V?l G0001;R*ZlE From a6fb92f17962cb8811782921163561b94cb7a6bd Mon Sep 17 00:00:00 2001 From: Simone Date: Fri, 23 Jun 2023 09:19:31 +0200 Subject: [PATCH 14/23] 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 8ac5b44629..3b7bb280c8 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 @@ 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: + # 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 15/23] 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 3b7bb280c8..fe72e57809 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 @@ def _analyze_top_level_function( 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 16/23] 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 b694d1044a..307e6736ff 100644 --- a/tests/e2e/solc_parsing/test_ast_parsing.py +++ b/tests/e2e/solc_parsing/test_ast_parsing.py @@ -458,6 +458,7 @@ def make_version(minor: int, patch_min: int, patch_max: int) -> List[str]: "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: From ace672e27d1e97375cc7359b1f7a05d240a6ca6d Mon Sep 17 00:00:00 2001 From: Simone Date: Fri, 23 Jun 2023 09:41:29 +0200 Subject: [PATCH 17/23] Rename tests to run to ALL_TESTS --- tests/e2e/detectors/test_detectors.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/e2e/detectors/test_detectors.py b/tests/e2e/detectors/test_detectors.py index d003e7ce00..a34f1932ea 100644 --- a/tests/e2e/detectors/test_detectors.py +++ b/tests/e2e/detectors/test_detectors.py @@ -56,7 +56,7 @@ def id_test(test_item: Test): return f"{test_item.detector.__name__}-{test_item.solc_ver}-{test_item.test_file}" -ALL_TEST_OBJECTS = [ +ALL_TESTS = [ Test( all_detectors.UninitializedFunctionPtrsConstructor, "uninitialized_function_ptr_constructor.sol", @@ -1656,7 +1656,7 @@ def id_test(test_item: Test): TEST_DATA_DIR = Path(__file__).resolve().parent / "test_data" # pylint: disable=too-many-locals -@pytest.mark.parametrize("test_item", ALL_TEST_OBJECTS, ids=id_test) +@pytest.mark.parametrize("test_item", ALL_TESTS, ids=id_test) def test_detector(test_item: Test, snapshot): test_dir_path = Path( TEST_DATA_DIR, @@ -1704,5 +1704,5 @@ def _generate_compile(test_item: Test, skip_existing=False): "To generate the zip artifacts run\n\tpython tests/e2e/tests/test_detectors.py --compile" ) elif sys.argv[1] == "--compile": - for next_test in ALL_TEST_OBJECTS: + for next_test in ALL_TESTS: _generate_compile(next_test, skip_existing=True) From 940a0a17d911af07dc2994dc224cdc1fc034973d Mon Sep 17 00:00:00 2001 From: Simone Date: Fri, 23 Jun 2023 09:42:49 +0200 Subject: [PATCH 18/23] Update CONTRIBUTING.md --- CONTRIBUTING.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c00fda8aad..0ebaa8d052 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -81,7 +81,7 @@ For each new detector, at least one regression tests must be present. 1. Create a folder in `tests/e2e/detectors/test_data` with the detector's argument name. 2. Create a test contract in `tests/e2e/detectors/test_data//`. -3. Update `ALL_TEST` in `tests/e2e/detectors/test_detectors.py` +3. Update `ALL_TESTS` in `tests/e2e/detectors/test_detectors.py` 4. Run `python tests/e2e/detectors/test_detectors.py --compile` to create a zip file of the compilation artifacts. 5. `pytest tests/e2e/detectors/test_detectors.py --insta update-new`. This will generate a snapshot of the detector output in `tests/e2e/detectors/snapshots/`. If updating an existing detector, run `pytest tests/e2e/detectors/test_detectors.py --insta review` and accept or reject the updates. 6. Run `pytest tests/e2e/detectors/test_detectors.py` to ensure everything worked. Then, add and commit the files to git. @@ -97,8 +97,9 @@ For each new detector, at least one regression tests must be present. 1. Create a test in `tests/e2e/solc_parsing/` 2. Run `python tests/e2e/solc_parsing/test_ast_parsing.py --compile`. This will compile the artifact in `tests/e2e/solc_parsing/compile`. Add the compiled artifact to git. -3. Run `python tests/e2e/solc_parsing/test_ast_parsing.py --generate`. This will generate the json artifacts in `tests/e2e/solc_parsing/expected_json`. Add the generated files to git. -4. Run `pytest tests/e2e/solc_parsing/test_ast_parsing.py` and check that everything worked. +3. Update `ALL_TESTS` in `tests/e2e/solc_parsing/test_ast_parsing.py` +4. Run `python tests/e2e/solc_parsing/test_ast_parsing.py --generate`. This will generate the json artifacts in `tests/e2e/solc_parsing/expected_json`. Add the generated files to git. +5. Run `pytest tests/e2e/solc_parsing/test_ast_parsing.py` and check that everything worked. > ##### Helpful commands for parsing tests > From e1fd6702f9bb41a16e664c3bcad340cbc3046e6c Mon Sep 17 00:00:00 2001 From: Simone Date: Fri, 23 Jun 2023 09:48:08 +0200 Subject: [PATCH 19/23] Minor --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0ebaa8d052..5cf02136bd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -81,7 +81,7 @@ For each new detector, at least one regression tests must be present. 1. Create a folder in `tests/e2e/detectors/test_data` with the detector's argument name. 2. Create a test contract in `tests/e2e/detectors/test_data//`. -3. Update `ALL_TESTS` in `tests/e2e/detectors/test_detectors.py` +3. Update `ALL_TESTS` in `tests/e2e/detectors/test_detectors.py`. 4. Run `python tests/e2e/detectors/test_detectors.py --compile` to create a zip file of the compilation artifacts. 5. `pytest tests/e2e/detectors/test_detectors.py --insta update-new`. This will generate a snapshot of the detector output in `tests/e2e/detectors/snapshots/`. If updating an existing detector, run `pytest tests/e2e/detectors/test_detectors.py --insta review` and accept or reject the updates. 6. Run `pytest tests/e2e/detectors/test_detectors.py` to ensure everything worked. Then, add and commit the files to git. @@ -97,7 +97,7 @@ For each new detector, at least one regression tests must be present. 1. Create a test in `tests/e2e/solc_parsing/` 2. Run `python tests/e2e/solc_parsing/test_ast_parsing.py --compile`. This will compile the artifact in `tests/e2e/solc_parsing/compile`. Add the compiled artifact to git. -3. Update `ALL_TESTS` in `tests/e2e/solc_parsing/test_ast_parsing.py` +3. Update `ALL_TESTS` in `tests/e2e/solc_parsing/test_ast_parsing.py`. 4. Run `python tests/e2e/solc_parsing/test_ast_parsing.py --generate`. This will generate the json artifacts in `tests/e2e/solc_parsing/expected_json`. Add the generated files to git. 5. Run `pytest tests/e2e/solc_parsing/test_ast_parsing.py` and check that everything worked. From 8b0fd32cbe32b6e873bccc07de5231f8793e1686 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Fri, 23 Jun 2023 09:33:05 -0500 Subject: [PATCH 20/23] use enum instead of value in config, lint --- slither/__main__.py | 1 - slither/utils/command_line.py | 18 +++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/slither/__main__.py b/slither/__main__.py index 4fcfd896d6..d9201a90d9 100644 --- a/slither/__main__.py +++ b/slither/__main__.py @@ -36,7 +36,6 @@ from slither.utils.colors import red, set_colorization_enabled from slither.utils.command_line import ( FailOnLevel, - migrate_config_options, output_detectors, output_results_to_markdown, output_detectors_json, diff --git a/slither/utils/command_line.py b/slither/utils/command_line.py index 8518ada4a6..0824725821 100644 --- a/slither/utils/command_line.py +++ b/slither/utils/command_line.py @@ -54,7 +54,7 @@ class FailOnLevel(enum.Enum): "exclude_low": False, "exclude_medium": False, "exclude_high": False, - "fail_on": FailOnLevel.PEDANTIC.value, + "fail_on": FailOnLevel.PEDANTIC, "json": None, "sarif": None, "json-types": ",".join(DEFAULT_JSON_OUTPUT_TYPES), @@ -118,22 +118,22 @@ def migrate_config_options(args: argparse.Namespace, key: str, elem): if key.startswith("fail_") and getattr(args, "fail_on") == defaults_flag_in_config["fail_on"]: if key == "fail_pedantic": pedantic_setting = elem - fail_on = pedantic_setting and FailOnLevel.PEDANTIC or FailOnLevel.NONE + fail_on = FailOnLevel.PEDANTIC if pedantic_setting else FailOnLevel.NONE setattr(args, "fail_on", fail_on) - logger.info( - "Migrating fail_pedantic: {} as fail_on: {}".format(pedantic_setting, fail_on.value) - ) - elif key == "fail_low" and elem == True: + logger.info(f"Migrating fail_pedantic: {pedantic_setting} as fail_on: {fail_on.value}") + elif key == "fail_low" and elem is True: logger.info("Migrating fail_low: true -> fail_on: low") setattr(args, "fail_on", FailOnLevel.LOW) - elif key == "fail_medium" and elem == True: + + elif key == "fail_medium" and elem is True: logger.info("Migrating fail_medium: true -> fail_on: medium") setattr(args, "fail_on", FailOnLevel.MEDIUM) - elif key == "fail_high" and elem == True: + + elif key == "fail_high" and elem is True: logger.info("Migrating fail_high: true -> fail_on: high") setattr(args, "fail_on", FailOnLevel.HIGH) else: - logger.warn(yellow("Key {} was deprecated but no migration was provided".format(key))) + logger.warning(yellow(f"Key {key} was deprecated but no migration was provided")) def output_to_markdown( From 272c80182799cc9303b7f4185c191d7721e97dd7 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Fri, 23 Jun 2023 16:19:14 -0500 Subject: [PATCH 21/23] update readme's --- README.md | 210 ++++++++++++++------------- slither/tools/interface/README.md | 21 +++ slither/tools/interface/__main__.py | 9 +- slither/tools/read_storage/README.md | 33 +++-- 4 files changed, 159 insertions(+), 114 deletions(-) create mode 100644 slither/tools/interface/README.md diff --git a/README.md b/README.md index bce20bfb07..503f99f00c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Slither, the Solidity source analyzer + Logo [![Build Status](https://img.shields.io/github/actions/workflow/status/crytic/slither/ci.yml?branch=master)](https://github.com/crytic/slither/actions?query=workflow%3ACI) @@ -20,26 +21,29 @@ Slither is a Solidity static analysis framework written in Python3. It runs a su ## Features -* Detects vulnerable Solidity code with low false positives (see the list of [trophies](./trophies.md)) -* Identifies where the error condition occurs in the source code -* Easily integrates into continuous integration and Hardhat/Foundry builds -* Built-in 'printers' quickly report crucial contract information -* Detector API to write custom analyses in Python -* Ability to analyze contracts written with Solidity >= 0.4 -* Intermediate representation ([SlithIR](https://github.com/trailofbits/slither/wiki/SlithIR)) enables simple, high-precision analyses -* Correctly parses 99.9% of all public Solidity code -* Average execution time of less than 1 second per contract -* Integrates with Github's code scanning in [CI](https://github.com/marketplace/actions/slither-action) +- Detects vulnerable Solidity code with low false positives (see the list of [trophies](./trophies.md)) +- Identifies where the error condition occurs in the source code +- Easily integrates into continuous integration and Hardhat/Foundry builds +- Built-in 'printers' quickly report crucial contract information +- Detector API to write custom analyses in Python +- Ability to analyze contracts written with Solidity >= 0.4 +- Intermediate representation ([SlithIR](https://github.com/trailofbits/slither/wiki/SlithIR)) enables simple, high-precision analyses +- Correctly parses 99.9% of all public Solidity code +- Average execution time of less than 1 second per contract +- Integrates with Github's code scanning in [CI](https://github.com/marketplace/actions/slither-action) ## Usage Run Slither on a Hardhat/Foundry/Dapp/Brownie application: + ```bash slither . ``` + This is the preferred option if your project has dependencies as Slither relies on the underlying compilation framework to compile source code. However, you can run Slither on a single file that does not import dependencies: + ```bash slither tests/uninitialized.sol ``` @@ -79,118 +83,122 @@ docker run -it -v /home/share:/share trailofbits/eth-security-toolbox ``` ### Integration + - For GitHub action integration, use [slither-action](https://github.com/marketplace/actions/slither-action). - To generate a Markdown report, use `slither [target] --checklist`. - To generate a Markdown with GitHub source code highlighting, use `slither [target] --checklist --markdown-root https://github.com/ORG/REPO/blob/COMMIT/` (replace `ORG`, `REPO`, `COMMIT`) ## Detectors - Num | Detector | What it Detects | Impact | Confidence --- | --- | --- | --- | --- 1 | `abiencoderv2-array` | [Storage abiencoderv2 array](https://github.com/crytic/slither/wiki/Detector-Documentation#storage-abiencoderv2-array) | High | High 2 | `arbitrary-send-erc20` | [transferFrom uses arbitrary `from`](https://github.com/crytic/slither/wiki/Detector-Documentation#arbitrary-from-in-transferfrom) | High | High 3 | `array-by-reference` | [Modifying storage array by value](https://github.com/crytic/slither/wiki/Detector-Documentation#modifying-storage-array-by-value) | High | High -4 | `incorrect-shift` | [The order of parameters in a shift instruction is incorrect.](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-shift-in-assembly) | High | High -5 | `multiple-constructors` | [Multiple constructor schemes](https://github.com/crytic/slither/wiki/Detector-Documentation#multiple-constructor-schemes) | High | High -6 | `name-reused` | [Contract's name reused](https://github.com/crytic/slither/wiki/Detector-Documentation#name-reused) | High | High -7 | `protected-vars` | [Detected unprotected variables](https://github.com/crytic/slither/wiki/Detector-Documentation#protected-variables) | High | High -8 | `public-mappings-nested` | [Public mappings with nested variables](https://github.com/crytic/slither/wiki/Detector-Documentation#public-mappings-with-nested-variables) | High | High -9 | `rtlo` | [Right-To-Left-Override control character is used](https://github.com/crytic/slither/wiki/Detector-Documentation#right-to-left-override-character) | High | High -10 | `shadowing-state` | [State variables shadowing](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variable-shadowing) | High | High -11 | `suicidal` | [Functions allowing anyone to destruct the contract](https://github.com/crytic/slither/wiki/Detector-Documentation#suicidal) | High | High -12 | `uninitialized-state` | [Uninitialized state variables](https://github.com/crytic/slither/wiki/Detector-Documentation#uninitialized-state-variables) | High | High -13 | `uninitialized-storage` | [Uninitialized storage variables](https://github.com/crytic/slither/wiki/Detector-Documentation#uninitialized-storage-variables) | High | High -14 | `unprotected-upgrade` | [Unprotected upgradeable contract](https://github.com/crytic/slither/wiki/Detector-Documentation#unprotected-upgradeable-contract) | High | High -15 | `codex` | [Use Codex to find vulnerabilities.](https://github.com/crytic/slither/wiki/Detector-Documentation#codex) | High | Low -16 | `arbitrary-send-erc20-permit` | [transferFrom uses arbitrary from with permit](https://github.com/crytic/slither/wiki/Detector-Documentation#arbitrary-from-in-transferfrom-used-with-permit) | High | Medium -17 | `arbitrary-send-eth` | [Functions that send Ether to arbitrary destinations](https://github.com/crytic/slither/wiki/Detector-Documentation#functions-that-send-ether-to-arbitrary-destinations) | High | Medium -18 | `controlled-array-length` | [Tainted array length assignment](https://github.com/crytic/slither/wiki/Detector-Documentation#array-length-assignment) | High | Medium -19 | `controlled-delegatecall` | [Controlled delegatecall destination](https://github.com/crytic/slither/wiki/Detector-Documentation#controlled-delegatecall) | High | Medium -20 | `delegatecall-loop` | [Payable functions using `delegatecall` inside a loop](https://github.com/crytic/slither/wiki/Detector-Documentation/#payable-functions-using-delegatecall-inside-a-loop) | High | Medium -21 | `msg-value-loop` | [msg.value inside a loop](https://github.com/crytic/slither/wiki/Detector-Documentation/#msgvalue-inside-a-loop) | High | Medium -22 | `reentrancy-eth` | [Reentrancy vulnerabilities (theft of ethers)](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities) | High | Medium -23 | `storage-array` | [Signed storage integer array compiler bug](https://github.com/crytic/slither/wiki/Detector-Documentation#storage-signed-integer-array) | High | Medium -24 | `unchecked-transfer` | [Unchecked tokens transfer](https://github.com/crytic/slither/wiki/Detector-Documentation#unchecked-transfer) | High | Medium -25 | `weak-prng` | [Weak PRNG](https://github.com/crytic/slither/wiki/Detector-Documentation#weak-PRNG) | High | Medium -26 | `domain-separator-collision` | [Detects ERC20 tokens that have a function whose signature collides with EIP-2612's DOMAIN_SEPARATOR()](https://github.com/crytic/slither/wiki/Detector-Documentation#domain-separator-collision) | Medium | High -27 | `enum-conversion` | [Detect dangerous enum conversion](https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-enum-conversion) | Medium | High -28 | `erc20-interface` | [Incorrect ERC20 interfaces](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-erc20-interface) | Medium | High -29 | `erc721-interface` | [Incorrect ERC721 interfaces](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-erc721-interface) | Medium | High -30 | `incorrect-equality` | [Dangerous strict equalities](https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities) | Medium | High -31 | `locked-ether` | [Contracts that lock ether](https://github.com/crytic/slither/wiki/Detector-Documentation#contracts-that-lock-ether) | Medium | High -32 | `mapping-deletion` | [Deletion on mapping containing a structure](https://github.com/crytic/slither/wiki/Detector-Documentation#deletion-on-mapping-containing-a-structure) | Medium | High -33 | `shadowing-abstract` | [State variables shadowing from abstract contracts](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variable-shadowing-from-abstract-contracts) | Medium | High -34 | `tautology` | [Tautology or contradiction](https://github.com/crytic/slither/wiki/Detector-Documentation#tautology-or-contradiction) | Medium | High -35 | `write-after-write` | [Unused write](https://github.com/crytic/slither/wiki/Detector-Documentation#write-after-write) | Medium | High -36 | `boolean-cst` | [Misuse of Boolean constant](https://github.com/crytic/slither/wiki/Detector-Documentation#misuse-of-a-boolean-constant) | Medium | Medium -37 | `constant-function-asm` | [Constant functions using assembly code](https://github.com/crytic/slither/wiki/Detector-Documentation#constant-functions-using-assembly-code) | Medium | Medium -38 | `constant-function-state` | [Constant functions changing the state](https://github.com/crytic/slither/wiki/Detector-Documentation#constant-functions-changing-the-state) | Medium | Medium -39 | `divide-before-multiply` | [Imprecise arithmetic operations order](https://github.com/crytic/slither/wiki/Detector-Documentation#divide-before-multiply) | Medium | Medium -40 | `reentrancy-no-eth` | [Reentrancy vulnerabilities (no theft of ethers)](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-1) | Medium | Medium -41 | `reused-constructor` | [Reused base constructor](https://github.com/crytic/slither/wiki/Detector-Documentation#reused-base-constructors) | Medium | Medium -42 | `tx-origin` | [Dangerous usage of `tx.origin`](https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-usage-of-txorigin) | Medium | Medium -43 | `unchecked-lowlevel` | [Unchecked low-level calls](https://github.com/crytic/slither/wiki/Detector-Documentation#unchecked-low-level-calls) | Medium | Medium -44 | `unchecked-send` | [Unchecked send](https://github.com/crytic/slither/wiki/Detector-Documentation#unchecked-send) | Medium | Medium -45 | `uninitialized-local` | [Uninitialized local variables](https://github.com/crytic/slither/wiki/Detector-Documentation#uninitialized-local-variables) | Medium | Medium -46 | `unused-return` | [Unused return values](https://github.com/crytic/slither/wiki/Detector-Documentation#unused-return) | Medium | Medium -47 | `incorrect-modifier` | [Modifiers that can return the default value](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-modifier) | Low | High -48 | `shadowing-builtin` | [Built-in symbol shadowing](https://github.com/crytic/slither/wiki/Detector-Documentation#builtin-symbol-shadowing) | Low | High -49 | `shadowing-local` | [Local variables shadowing](https://github.com/crytic/slither/wiki/Detector-Documentation#local-variable-shadowing) | Low | High -50 | `uninitialized-fptr-cst` | [Uninitialized function pointer calls in constructors](https://github.com/crytic/slither/wiki/Detector-Documentation#uninitialized-function-pointers-in-constructors) | Low | High -51 | `variable-scope` | [Local variables used prior their declaration](https://github.com/crytic/slither/wiki/Detector-Documentation#pre-declaration-usage-of-local-variables) | Low | High -52 | `void-cst` | [Constructor called not implemented](https://github.com/crytic/slither/wiki/Detector-Documentation#void-constructor) | Low | High -53 | `calls-loop` | [Multiple calls in a loop](https://github.com/crytic/slither/wiki/Detector-Documentation/#calls-inside-a-loop) | Low | Medium -54 | `events-access` | [Missing Events Access Control](https://github.com/crytic/slither/wiki/Detector-Documentation#missing-events-access-control) | Low | Medium -55 | `events-maths` | [Missing Events Arithmetic](https://github.com/crytic/slither/wiki/Detector-Documentation#missing-events-arithmetic) | Low | Medium -56 | `incorrect-unary` | [Dangerous unary expressions](https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-unary-expressions) | Low | Medium -57 | `missing-zero-check` | [Missing Zero Address Validation](https://github.com/crytic/slither/wiki/Detector-Documentation#missing-zero-address-validation) | Low | Medium -58 | `reentrancy-benign` | [Benign reentrancy vulnerabilities](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-2) | Low | Medium -59 | `reentrancy-events` | [Reentrancy vulnerabilities leading to out-of-order Events](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-3) | Low | Medium -60 | `timestamp` | [Dangerous usage of `block.timestamp`](https://github.com/crytic/slither/wiki/Detector-Documentation#block-timestamp) | Low | Medium -61 | `assembly` | [Assembly usage](https://github.com/crytic/slither/wiki/Detector-Documentation#assembly-usage) | Informational | High -62 | `assert-state-change` | [Assert state change](https://github.com/crytic/slither/wiki/Detector-Documentation#assert-state-change) | Informational | High -63 | `boolean-equal` | [Comparison to boolean constant](https://github.com/crytic/slither/wiki/Detector-Documentation#boolean-equality) | Informational | High -64 | `cyclomatic-complexity` | [Detects functions with high (> 11) cyclomatic complexity](https://github.com/crytic/slither/wiki/Detector-Documentation#cyclomatic-complexity) | Informational | High -65 | `deprecated-standards` | [Deprecated Solidity Standards](https://github.com/crytic/slither/wiki/Detector-Documentation#deprecated-standards) | Informational | High -66 | `erc20-indexed` | [Un-indexed ERC20 event parameters](https://github.com/crytic/slither/wiki/Detector-Documentation#unindexed-erc20-event-parameters) | Informational | High -67 | `function-init-state` | [Function initializing state variables](https://github.com/crytic/slither/wiki/Detector-Documentation#function-initializing-state) | Informational | High -68 | `low-level-calls` | [Low level calls](https://github.com/crytic/slither/wiki/Detector-Documentation#low-level-calls) | Informational | High -69 | `missing-inheritance` | [Missing inheritance](https://github.com/crytic/slither/wiki/Detector-Documentation#missing-inheritance) | Informational | High -70 | `naming-convention` | [Conformity to Solidity naming conventions](https://github.com/crytic/slither/wiki/Detector-Documentation#conformance-to-solidity-naming-conventions) | Informational | High -71 | `pragma` | [If different pragma directives are used](https://github.com/crytic/slither/wiki/Detector-Documentation#different-pragma-directives-are-used) | Informational | High -72 | `redundant-statements` | [Redundant statements](https://github.com/crytic/slither/wiki/Detector-Documentation#redundant-statements) | Informational | High -73 | `solc-version` | [Incorrect Solidity version](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-versions-of-solidity) | Informational | High -74 | `unimplemented-functions` | [Unimplemented functions](https://github.com/crytic/slither/wiki/Detector-Documentation#unimplemented-functions) | Informational | High -75 | `unused-state` | [Unused state variables](https://github.com/crytic/slither/wiki/Detector-Documentation#unused-state-variable) | Informational | High -76 | `costly-loop` | [Costly operations in a loop](https://github.com/crytic/slither/wiki/Detector-Documentation#costly-operations-inside-a-loop) | Informational | Medium -77 | `dead-code` | [Functions that are not used](https://github.com/crytic/slither/wiki/Detector-Documentation#dead-code) | Informational | Medium -78 | `reentrancy-unlimited-gas` | [Reentrancy vulnerabilities through send and transfer](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-4) | Informational | Medium -79 | `similar-names` | [Variable names are too similar](https://github.com/crytic/slither/wiki/Detector-Documentation#variable-names-too-similar) | Informational | Medium -80 | `too-many-digits` | [Conformance to numeric notation best practices](https://github.com/crytic/slither/wiki/Detector-Documentation#too-many-digits) | Informational | Medium -81 | `constable-states` | [State variables that could be declared constant](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variables-that-could-be-declared-constant) | Optimization | High -82 | `external-function` | [Public function that could be declared external](https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-external) | Optimization | High -83 | `immutable-states` | [State variables that could be declared immutable](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variables-that-could-be-declared-immutable) | Optimization | High -84 | `var-read-using-this` | [Contract reads its own variable using `this`](https://github.com/crytic/slither/wiki/Vulnerabilities-Description#public-variable-read-in-external-context) | Optimization | High +4 | `encode-packed-collision` | [ABI encodePacked Collision](https://github.com/crytic/slither/wiki/Detector-Documentation#abi-encodePacked-collision) | High | High +5 | `incorrect-shift` | [The order of parameters in a shift instruction is incorrect.](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-shift-in-assembly) | High | High +6 | `multiple-constructors` | [Multiple constructor schemes](https://github.com/crytic/slither/wiki/Detector-Documentation#multiple-constructor-schemes) | High | High +7 | `name-reused` | [Contract's name reused](https://github.com/crytic/slither/wiki/Detector-Documentation#name-reused) | High | High +8 | `protected-vars` | [Detected unprotected variables](https://github.com/crytic/slither/wiki/Detector-Documentation#protected-variables) | High | High +9 | `public-mappings-nested` | [Public mappings with nested variables](https://github.com/crytic/slither/wiki/Detector-Documentation#public-mappings-with-nested-variables) | High | High +10 | `rtlo` | [Right-To-Left-Override control character is used](https://github.com/crytic/slither/wiki/Detector-Documentation#right-to-left-override-character) | High | High +11 | `shadowing-state` | [State variables shadowing](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variable-shadowing) | High | High +12 | `suicidal` | [Functions allowing anyone to destruct the contract](https://github.com/crytic/slither/wiki/Detector-Documentation#suicidal) | High | High +13 | `uninitialized-state` | [Uninitialized state variables](https://github.com/crytic/slither/wiki/Detector-Documentation#uninitialized-state-variables) | High | High +14 | `uninitialized-storage` | [Uninitialized storage variables](https://github.com/crytic/slither/wiki/Detector-Documentation#uninitialized-storage-variables) | High | High +15 | `unprotected-upgrade` | [Unprotected upgradeable contract](https://github.com/crytic/slither/wiki/Detector-Documentation#unprotected-upgradeable-contract) | High | High +16 | `codex` | [Use Codex to find vulnerabilities.](https://github.com/crytic/slither/wiki/Detector-Documentation#codex) | High | Low +17 | `arbitrary-send-erc20-permit` | [transferFrom uses arbitrary from with permit](https://github.com/crytic/slither/wiki/Detector-Documentation#arbitrary-from-in-transferfrom-used-with-permit) | High | Medium +18 | `arbitrary-send-eth` | [Functions that send Ether to arbitrary destinations](https://github.com/crytic/slither/wiki/Detector-Documentation#functions-that-send-ether-to-arbitrary-destinations) | High | Medium +19 | `controlled-array-length` | [Tainted array length assignment](https://github.com/crytic/slither/wiki/Detector-Documentation#array-length-assignment) | High | Medium +20 | `controlled-delegatecall` | [Controlled delegatecall destination](https://github.com/crytic/slither/wiki/Detector-Documentation#controlled-delegatecall) | High | Medium +21 | `delegatecall-loop` | [Payable functions using `delegatecall` inside a loop](https://github.com/crytic/slither/wiki/Detector-Documentation/#payable-functions-using-delegatecall-inside-a-loop) | High | Medium +22 | `msg-value-loop` | [msg.value inside a loop](https://github.com/crytic/slither/wiki/Detector-Documentation/#msgvalue-inside-a-loop) | High | Medium +23 | `reentrancy-eth` | [Reentrancy vulnerabilities (theft of ethers)](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities) | High | Medium +24 | `storage-array` | [Signed storage integer array compiler bug](https://github.com/crytic/slither/wiki/Detector-Documentation#storage-signed-integer-array) | High | Medium +25 | `unchecked-transfer` | [Unchecked tokens transfer](https://github.com/crytic/slither/wiki/Detector-Documentation#unchecked-transfer) | High | Medium +26 | `weak-prng` | [Weak PRNG](https://github.com/crytic/slither/wiki/Detector-Documentation#weak-PRNG) | High | Medium +27 | `domain-separator-collision` | [Detects ERC20 tokens that have a function whose signature collides with EIP-2612's DOMAIN_SEPARATOR()](https://github.com/crytic/slither/wiki/Detector-Documentation#domain-separator-collision) | Medium | High +28 | `enum-conversion` | [Detect dangerous enum conversion](https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-enum-conversion) | Medium | High +29 | `erc20-interface` | [Incorrect ERC20 interfaces](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-erc20-interface) | Medium | High +30 | `erc721-interface` | [Incorrect ERC721 interfaces](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-erc721-interface) | Medium | High +31 | `incorrect-equality` | [Dangerous strict equalities](https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities) | Medium | High +32 | `locked-ether` | [Contracts that lock ether](https://github.com/crytic/slither/wiki/Detector-Documentation#contracts-that-lock-ether) | Medium | High +33 | `mapping-deletion` | [Deletion on mapping containing a structure](https://github.com/crytic/slither/wiki/Detector-Documentation#deletion-on-mapping-containing-a-structure) | Medium | High +34 | `shadowing-abstract` | [State variables shadowing from abstract contracts](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variable-shadowing-from-abstract-contracts) | Medium | High +35 | `tautology` | [Tautology or contradiction](https://github.com/crytic/slither/wiki/Detector-Documentation#tautology-or-contradiction) | Medium | High +36 | `write-after-write` | [Unused write](https://github.com/crytic/slither/wiki/Detector-Documentation#write-after-write) | Medium | High +37 | `boolean-cst` | [Misuse of Boolean constant](https://github.com/crytic/slither/wiki/Detector-Documentation#misuse-of-a-boolean-constant) | Medium | Medium +38 | `constant-function-asm` | [Constant functions using assembly code](https://github.com/crytic/slither/wiki/Detector-Documentation#constant-functions-using-assembly-code) | Medium | Medium +39 | `constant-function-state` | [Constant functions changing the state](https://github.com/crytic/slither/wiki/Detector-Documentation#constant-functions-changing-the-state) | Medium | Medium +40 | `divide-before-multiply` | [Imprecise arithmetic operations order](https://github.com/crytic/slither/wiki/Detector-Documentation#divide-before-multiply) | Medium | Medium +41 | `reentrancy-no-eth` | [Reentrancy vulnerabilities (no theft of ethers)](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-1) | Medium | Medium +42 | `reused-constructor` | [Reused base constructor](https://github.com/crytic/slither/wiki/Detector-Documentation#reused-base-constructors) | Medium | Medium +43 | `tx-origin` | [Dangerous usage of `tx.origin`](https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-usage-of-txorigin) | Medium | Medium +44 | `unchecked-lowlevel` | [Unchecked low-level calls](https://github.com/crytic/slither/wiki/Detector-Documentation#unchecked-low-level-calls) | Medium | Medium +45 | `unchecked-send` | [Unchecked send](https://github.com/crytic/slither/wiki/Detector-Documentation#unchecked-send) | Medium | Medium +46 | `uninitialized-local` | [Uninitialized local variables](https://github.com/crytic/slither/wiki/Detector-Documentation#uninitialized-local-variables) | Medium | Medium +47 | `unused-return` | [Unused return values](https://github.com/crytic/slither/wiki/Detector-Documentation#unused-return) | Medium | Medium +48 | `incorrect-modifier` | [Modifiers that can return the default value](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-modifier) | Low | High +49 | `shadowing-builtin` | [Built-in symbol shadowing](https://github.com/crytic/slither/wiki/Detector-Documentation#builtin-symbol-shadowing) | Low | High +50 | `shadowing-local` | [Local variables shadowing](https://github.com/crytic/slither/wiki/Detector-Documentation#local-variable-shadowing) | Low | High +51 | `uninitialized-fptr-cst` | [Uninitialized function pointer calls in constructors](https://github.com/crytic/slither/wiki/Detector-Documentation#uninitialized-function-pointers-in-constructors) | Low | High +52 | `variable-scope` | [Local variables used prior their declaration](https://github.com/crytic/slither/wiki/Detector-Documentation#pre-declaration-usage-of-local-variables) | Low | High +53 | `void-cst` | [Constructor called not implemented](https://github.com/crytic/slither/wiki/Detector-Documentation#void-constructor) | Low | High +54 | `calls-loop` | [Multiple calls in a loop](https://github.com/crytic/slither/wiki/Detector-Documentation/#calls-inside-a-loop) | Low | Medium +55 | `events-access` | [Missing Events Access Control](https://github.com/crytic/slither/wiki/Detector-Documentation#missing-events-access-control) | Low | Medium +56 | `events-maths` | [Missing Events Arithmetic](https://github.com/crytic/slither/wiki/Detector-Documentation#missing-events-arithmetic) | Low | Medium +57 | `incorrect-unary` | [Dangerous unary expressions](https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-unary-expressions) | Low | Medium +58 | `missing-zero-check` | [Missing Zero Address Validation](https://github.com/crytic/slither/wiki/Detector-Documentation#missing-zero-address-validation) | Low | Medium +59 | `reentrancy-benign` | [Benign reentrancy vulnerabilities](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-2) | Low | Medium +60 | `reentrancy-events` | [Reentrancy vulnerabilities leading to out-of-order Events](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-3) | Low | Medium +61 | `timestamp` | [Dangerous usage of `block.timestamp`](https://github.com/crytic/slither/wiki/Detector-Documentation#block-timestamp) | Low | Medium +62 | `assembly` | [Assembly usage](https://github.com/crytic/slither/wiki/Detector-Documentation#assembly-usage) | Informational | High +63 | `assert-state-change` | [Assert state change](https://github.com/crytic/slither/wiki/Detector-Documentation#assert-state-change) | Informational | High +64 | `boolean-equal` | [Comparison to boolean constant](https://github.com/crytic/slither/wiki/Detector-Documentation#boolean-equality) | Informational | High +65 | `cyclomatic-complexity` | [Detects functions with high (> 11) cyclomatic complexity](https://github.com/crytic/slither/wiki/Detector-Documentation#cyclomatic-complexity) | Informational | High +66 | `deprecated-standards` | [Deprecated Solidity Standards](https://github.com/crytic/slither/wiki/Detector-Documentation#deprecated-standards) | Informational | High +67 | `erc20-indexed` | [Un-indexed ERC20 event parameters](https://github.com/crytic/slither/wiki/Detector-Documentation#unindexed-erc20-event-parameters) | Informational | High +68 | `function-init-state` | [Function initializing state variables](https://github.com/crytic/slither/wiki/Detector-Documentation#function-initializing-state) | Informational | High +69 | `incorrect-using-for` | [Detects using-for statement usage when no function from a given library matches a given type](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-using-for-usage) | Informational | High +70 | `low-level-calls` | [Low level calls](https://github.com/crytic/slither/wiki/Detector-Documentation#low-level-calls) | Informational | High +71 | `missing-inheritance` | [Missing inheritance](https://github.com/crytic/slither/wiki/Detector-Documentation#missing-inheritance) | Informational | High +72 | `naming-convention` | [Conformity to Solidity naming conventions](https://github.com/crytic/slither/wiki/Detector-Documentation#conformance-to-solidity-naming-conventions) | Informational | High +73 | `pragma` | [If different pragma directives are used](https://github.com/crytic/slither/wiki/Detector-Documentation#different-pragma-directives-are-used) | Informational | High +74 | `redundant-statements` | [Redundant statements](https://github.com/crytic/slither/wiki/Detector-Documentation#redundant-statements) | Informational | High +75 | `solc-version` | [Incorrect Solidity version](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-versions-of-solidity) | Informational | High +76 | `unimplemented-functions` | [Unimplemented functions](https://github.com/crytic/slither/wiki/Detector-Documentation#unimplemented-functions) | Informational | High +77 | `unused-state` | [Unused state variables](https://github.com/crytic/slither/wiki/Detector-Documentation#unused-state-variable) | Informational | High +78 | `costly-loop` | [Costly operations in a loop](https://github.com/crytic/slither/wiki/Detector-Documentation#costly-operations-inside-a-loop) | Informational | Medium +79 | `dead-code` | [Functions that are not used](https://github.com/crytic/slither/wiki/Detector-Documentation#dead-code) | Informational | Medium +80 | `reentrancy-unlimited-gas` | [Reentrancy vulnerabilities through send and transfer](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-4) | Informational | Medium +81 | `similar-names` | [Variable names are too similar](https://github.com/crytic/slither/wiki/Detector-Documentation#variable-names-too-similar) | Informational | Medium +82 | `too-many-digits` | [Conformance to numeric notation best practices](https://github.com/crytic/slither/wiki/Detector-Documentation#too-many-digits) | Informational | Medium +83 | `cache-array-length` | [Detects `for` loops that use `length` member of some storage array in their loop condition and don't modify it.](https://github.com/crytic/slither/wiki/Detector-Documentation#cache-array-length) | Optimization | High +84 | `constable-states` | [State variables that could be declared constant](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variables-that-could-be-declared-constant) | Optimization | High +85 | `external-function` | [Public function that could be declared external](https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-external) | Optimization | High +86 | `immutable-states` | [State variables that could be declared immutable](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variables-that-could-be-declared-immutable) | Optimization | High +87 | `var-read-using-this` | [Contract reads its own variable using `this`](https://github.com/crytic/slither/wiki/Detector-Documentation#public-variable-read-in-external-context) | Optimization | High For more information, see + - The [Detector Documentation](https://github.com/crytic/slither/wiki/Detector-Documentation) for details on each detector - The [Detection Selection](https://github.com/crytic/slither/wiki/Usage#detector-selection) to run only selected detectors. By default, all the detectors are run. - The [Triage Mode](https://github.com/crytic/slither/wiki/Usage#triage-mode) to filter individual results ## Printers - ### Quick Review Printers - `human-summary`: [Print a human-readable summary of the contracts](https://github.com/trailofbits/slither/wiki/Printer-documentation#human-summary) - `inheritance-graph`: [Export the inheritance graph of each contract to a dot file](https://github.com/trailofbits/slither/wiki/Printer-documentation#inheritance-graph) - `contract-summary`: [Print a summary of the contracts](https://github.com/trailofbits/slither/wiki/Printer-documentation#contract-summary) +15 | `loc` | [Count the total number lines of code (LOC), source lines of code (SLOC), and comment lines of code (CLOC) found in source files (SRC), dependencies (DEP), and test files (TEST).](https://github.com/trailofbits/slither/wiki/Printer-documentation#loc) ### In-Depth Review Printers - `call-graph`: [Export the call-graph of the contracts to a dot file](https://github.com/trailofbits/slither/wiki/Printer-documentation#call-graph) - `cfg`: [Export the CFG of each functions](https://github.com/trailofbits/slither/wiki/Printer-documentation#cfg) - `function-summary`: [Print a summary of the functions](https://github.com/trailofbits/slither/wiki/Printer-documentation#function-summary) - `vars-and-auth`: [Print the state variables written and the authorization of the functions](https://github.com/crytic/slither/wiki/Printer-documentation#variables-written-and-authorization) -- `when-not-paused`: [Print functions that do not use `whenNotPaused` modifier](https://github.com/trailofbits/slither/wiki/Printer-documentation#when-not-paused). +- `not-pausable`: [Print functions that do not use `whenNotPaused` modifier](https://github.com/trailofbits/slither/wiki/Printer-documentation#when-not-paused). To run a printer, use `--print` and a comma-separated list of printers. @@ -204,32 +212,36 @@ See the [Printer documentation](https://github.com/crytic/slither/wiki/Printer-d - `slither-check-erc`: [Check the ERC's conformance](https://github.com/crytic/slither/wiki/ERC-Conformance) - `slither-format`: [Automatic patch generation](https://github.com/crytic/slither/wiki/Slither-format) - `slither-read-storage`: [Read storage values from contracts](./slither/tools/read_storage/README.md) +- `slither-interface`: [Generate an interface for a contract](./slither/tools/interface/README.md) See the [Tool documentation](https://github.com/crytic/slither/wiki/Tool-Documentation) for additional tools. [Contact us](https://www.trailofbits.com/contact/) to get help on building custom tools. ## API Documentation + Documentation on Slither's internals is available [here](https://crytic.github.io/slither/slither.html). ## Getting Help Feel free to stop by our [Slack channel](https://empireslacking.herokuapp.com) (#ethereum) for help using or extending Slither. -* The [Printer documentation](https://github.com/trailofbits/slither/wiki/Printer-documentation) describes the information Slither is capable of visualizing for each contract. +- The [Printer documentation](https://github.com/trailofbits/slither/wiki/Printer-documentation) describes the information Slither is capable of visualizing for each contract. -* The [Detector documentation](https://github.com/trailofbits/slither/wiki/Adding-a-new-detector) describes how to write a new vulnerability analyses. +- The [Detector documentation](https://github.com/trailofbits/slither/wiki/Adding-a-new-detector) describes how to write a new vulnerability analyses. -* The [API documentation](https://github.com/crytic/slither/wiki/Python-API) describes the methods and objects available for custom analyses. +- The [API documentation](https://github.com/crytic/slither/wiki/Python-API) describes the methods and objects available for custom analyses. -* The [SlithIR documentation](https://github.com/trailofbits/slither/wiki/SlithIR) describes the SlithIR intermediate representation. +- The [SlithIR documentation](https://github.com/trailofbits/slither/wiki/SlithIR) describes the SlithIR intermediate representation. ## FAQ How do I exclude mocks or tests? + - View our documentation on [path filtering](https://github.com/crytic/slither/wiki/Usage#path-filtering). How do I fix "unknown file" or compilation issues? + - Because slither requires the solc AST, it must have all dependencies available. If a contract has dependencies, `slither contract.sol` will fail. Instead, use `slither .` in the parent directory of `contracts/` (you should see `contracts/` when you run `ls`). @@ -244,9 +256,11 @@ Slither is licensed and distributed under the AGPLv3 license. [Contact us](mailt ## Publications ### Trail of Bits publication + - [Slither: A Static Analysis Framework For Smart Contracts](https://arxiv.org/abs/1908.09878), Josselin Feist, Gustavo Grieco, Alex Groce - WETSEB '19 ### External publications + Title | Usage | Authors | Venue | Code --- | --- | --- | --- | --- [ReJection: A AST-Based Reentrancy Vulnerability Detection Method](https://www.researchgate.net/publication/339354823_ReJection_A_AST-Based_Reentrancy_Vulnerability_Detection_Method) | AST-based analysis built on top of Slither | Rui Ma, Zefeng Jian, Guangyuan Chen, Ke Ma, Yujia Chen | CTCIS 19 diff --git a/slither/tools/interface/README.md b/slither/tools/interface/README.md new file mode 100644 index 0000000000..a77e780b0a --- /dev/null +++ b/slither/tools/interface/README.md @@ -0,0 +1,21 @@ +# Slither-interface + +Generates code for a Solidity interface from contract + +## Usage + +Run `slither-interface `. + +## CLI Interface +```shell +positional arguments: + contract_source The name of the contract (case sensitive) followed by the deployed contract address if verified on etherscan or project directory/filename for local contracts. + +optional arguments: + -h, --help show this help message and exit + --unroll-structs Whether to use structures' underlying types instead of the user-defined type + --exclude-events Excludes event signatures in the interface + --exclude-errors Excludes custom error signatures in the interface + --exclude-enums Excludes enum definitions in the interface + --exclude-structs Exclude struct definitions in the interface +``` \ No newline at end of file diff --git a/slither/tools/interface/__main__.py b/slither/tools/interface/__main__.py index e56c4b3ebe..0705f0373a 100644 --- a/slither/tools/interface/__main__.py +++ b/slither/tools/interface/__main__.py @@ -86,10 +86,11 @@ def main() -> None: ) # add version pragma - interface = ( - f"pragma solidity {_contract.compilation_unit.pragma_directives[0].version};\n\n" - + interface - ) + if _contract.compilation_unit.pragma_directives: + interface = ( + f"pragma solidity {_contract.compilation_unit.pragma_directives[0].version};\n\n" + + interface + ) # write interface to file export = Path("crytic-export", "interfaces") diff --git a/slither/tools/read_storage/README.md b/slither/tools/read_storage/README.md index 677b2c772c..dd7ef28658 100644 --- a/slither/tools/read_storage/README.md +++ b/slither/tools/read_storage/README.md @@ -8,20 +8,29 @@ Slither-read-storage is a tool to retrieve the storage slots and values of entir ```shell positional arguments: - contract_source (DIR) ADDRESS The deployed contract address if verified on etherscan. Prepend project directory for unverified contracts. + contract_source The deployed contract address if verified on etherscan. Prepend project directory for unverified contracts. optional arguments: - --variable-name VARIABLE_NAME The name of the variable whose value will be returned. - --rpc-url RPC_URL An endpoint for web3 requests. - --key KEY The key/ index whose value will be returned from a mapping or array. - --deep-key DEEP_KEY The key/ index whose value will be returned from a deep mapping or multidimensional array. - --struct-var STRUCT_VAR The name of the variable whose value will be returned from a struct. - --storage-address STORAGE_ADDRESS The address of the storage contract (if a proxy pattern is used). - --contract-name CONTRACT_NAME The name of the logic contract. - --json FILE Write the entire storage layout in JSON format to the specified FILE - --value Toggle used to include values in output. - --max-depth MAX_DEPTH Max depth to search in data structure. - --block BLOCK_NUMBER Block number to retrieve storage from (requires archive rpc node) + -h, --help show this help message and exit + --variable-name VARIABLE_NAME + The name of the variable whose value will be returned. + --rpc-url RPC_URL An endpoint for web3 requests. + --key KEY The key/ index whose value will be returned from a mapping or array. + --deep-key DEEP_KEY The key/ index whose value will be returned from a deep mapping or multidimensional array. + --struct-var STRUCT_VAR + The name of the variable whose value will be returned from a struct. + --storage-address STORAGE_ADDRESS + The address of the storage contract (if a proxy pattern is used). + --contract-name CONTRACT_NAME + The name of the logic contract. + --json JSON Save the result in a JSON file. + --value Toggle used to include values in output. + --table Print table view of storage layout + --silent Silence log outputs + --max-depth MAX_DEPTH + Max depth to search in data structure. + --block BLOCK The block number to read storage from. Requires an archive node to be provided as the RPC url. + --unstructured Include unstructured storage slots ``` ### Examples From b6d1de43ca9abd1e9ed5e64da4cd6133fb240208 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Fri, 23 Jun 2023 16:31:26 -0500 Subject: [PATCH 22/23] fix bullet point --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 503f99f00c..17fdb02b5a 100644 --- a/README.md +++ b/README.md @@ -191,7 +191,7 @@ For more information, see - `human-summary`: [Print a human-readable summary of the contracts](https://github.com/trailofbits/slither/wiki/Printer-documentation#human-summary) - `inheritance-graph`: [Export the inheritance graph of each contract to a dot file](https://github.com/trailofbits/slither/wiki/Printer-documentation#inheritance-graph) - `contract-summary`: [Print a summary of the contracts](https://github.com/trailofbits/slither/wiki/Printer-documentation#contract-summary) -15 | `loc` | [Count the total number lines of code (LOC), source lines of code (SLOC), and comment lines of code (CLOC) found in source files (SRC), dependencies (DEP), and test files (TEST).](https://github.com/trailofbits/slither/wiki/Printer-documentation#loc) +- `loc`: [Count the total number lines of code (LOC), source lines of code (SLOC), and comment lines of code (CLOC) found in source files (SRC), dependencies (DEP), and test files (TEST).](https://github.com/trailofbits/slither/wiki/Printer-documentation#loc) ### In-Depth Review Printers - `call-graph`: [Export the call-graph of the contracts to a dot file](https://github.com/trailofbits/slither/wiki/Printer-documentation#call-graph) From 273cca4167ab0f057252c754a629e8d81af992c6 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Sat, 24 Jun 2023 10:50:11 -0500 Subject: [PATCH 23/23] fix logo on pypi (#1999) PyPI needs a link to properly render the logo xref https://github.com/pypi/warehouse/issues/5246 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 17fdb02b5a..cb815561e8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Slither, the Solidity source analyzer -Logo +Logo [![Build Status](https://img.shields.io/github/actions/workflow/status/crytic/slither/ci.yml?branch=master)](https://github.com/crytic/slither/actions?query=workflow%3ACI) [![Slack Status](https://empireslacking.herokuapp.com/badge.svg)](https://empireslacking.herokuapp.com)