From 0b7257209dd09b1d877ec40870dd55eaf8ab10d1 Mon Sep 17 00:00:00 2001 From: bart1e Date: Mon, 15 May 2023 21:01:04 +0200 Subject: [PATCH] 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 1f8111bdb..da73d3fd5 100644 --- a/slither/detectors/operations/cache_array_length.py +++ b/slither/detectors/operations/cache_array_length.py @@ -7,7 +7,7 @@ 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 +from slither.slithir.operations import Length, Delete, HighLevelCall class CacheArrayLength(AbstractDetector): @@ -119,8 +119,10 @@ contract C # - 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 @@ contract C 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 @@ contract C 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 @@ contract C ) 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 a0ba35740..63d4b883c 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 79858d182..704d6bed9 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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