# Analysis of Smart Contract Security Vulnerabilities and Tools       <br/>    <br/>  <br/>  ## SOLIDITY_GAS_LIMIT_IN_LOOPS ### Rule Description <p> Ethereum is a very resource-constrained environment. Prices per computational step are orders of magnitude higher than with centralized providers. Moreover, Ethereum miners impose a limit on the total number of gas consumed in a block. If <code>array.length</code> is large enough, the function exceeds the block gas limit, and transactions calling it will never be confirmed: </p> <pre><code> for (uint256 i = 0; i < array.length ; i++) { cosltyFunc(); } </code></pre> <p> This becomes a security issue, if an external actor influences <code>array.length</code>. E.g., if array enumerates all registered addresses, an adversary can register many addresses, causing the problem described above. </p> <p> Vulnerability type by SmartDec classification: <a href="https://github.com/smartdec/classification#gas-limitations"> Infinite loops</a>. </p> ### Solidity-Rules   ``` //forStatement [ <!-- i = 0; --> expression[1][text()[1] = "="]/expression[2]//numberLiteral/decimalNumber[text()[1] = "0"] <!-- uint i = 0; --> or expression[1]/variableDeclaration[text()[1] = "="]/expression//numberLiteral/decimalNumber[text()[1] = "0"] <!-- uint i; --> or expression[1]/variableDeclaration[not(text()[1] = "=")] ] [ <!-- ".length" in condition and base is not in memory --> condition/expression[matches(text()[1], "<|<=")]/expression[2][text()[1] = ".length"]/expression/primaryExpression/identifier <!-- and array is neither public or external function argument --> [not(text()[1] = ancestor::functionDefinition[visibleType[matches(text()[1], "^public$|^external$")] or not(visibleType)]//parameter/identifier/text())] <!-- nor internal or private function argument with memory visibility modifier --> [not(text()[1] = ancestor::functionDefinition[visibleType[matches(text()[1], "^private$|^internal$")]]//parameter[storageLocation/text() = "memory"]/identifier/text())] <!-- nor copied to local variable with memory visibility modifier --> [not(text()[1] = ancestor::functionDefinition//variableDeclaration[storageLocation/text() = "memory"]/identifier/text())] <!-- may be a variable in condition and it is ... --> or condition/expression[matches(text()[1], "<|< =")]/expression[2]/primaryExpression/identifier [ <!-- new variable with non-memory array length --> text()[1] = (ancestor::functionDefinition//variableDeclaration[text()[1] = "="] [expression[text()[1] = ".length"]/expression/primaryExpression/identifier <!-- and array is neither public or external function argument --> [not(text()[1] = ancestor::functionDefinition[visibleType[matches(text()[1], "^public$|^external$")] or not(visibleType)]//parameter/identifier/text())] <!-- nor internal or private function argument with memory visibility modifier --> [not(text()[1] = ancestor::functionDefinition[visibleType[matches(text()[1], "^private$|^internal$")]]//parameter[storageLocation/text() = "memory"]/identifier/text())] <!-- nor copied to local variable with memory visibility modifier --> [not(text()[1] = ancestor::functionDefinition//variableDeclaration[storageLocation/text() = "memory"]/identifier/text())] ]/identifier/text()[1]) <!-- or reused variable with non-memory array length --> or text()[1] = (ancestor::functionDefinition//expression[text()[1] = "="] [expression[2][text()[1] = ".length"]/expression/primaryExpression/identifier <!-- and array is neither public or external function argument --> [not(text()[1] = ancestor::functionDefinition[visibleType[matches(text()[1], "^public$|^external$")] or not(visibleType)]//parameter/identifier/text())] <!-- nor internal or private function argument with memory visibility modifier --> [not(text()[1] = ancestor::functionDefinition[visibleType[matches(text()[1], "^private$|^internal$")]]//parameter[storageLocation/text() = "memory"]/identifier/text())] <!-- nor copied to local variable with memory visibility modifier --> [not(text()[1] = ancestor::functionDefinition//variableDeclaration[storageLocation/text() = "memory"]/identifier/text())] ]/expression[1]//identifier/text()[1]) ] ] ```   ``` //forStatement [ <!-- i = 0; --> expression[1][text()[1] = "="]/expression[2]//numberLiteral/decimalNumber[text()[1] = "0"] <!-- uint i = 0; --> or expression[1]/variableDeclaration[text()[1] = "="]/expression//numberLiteral/decimalNumber[text()[1] = "0"] <!-- uint i; --> or expression[1]/variableDeclaration[not(text()[1] = "=")] ] [ <!-- ".length" in condition and base is in memory --> condition/expression[matches(text()[1], "<|<=")]/expression[2][text()[1] = ".length"]/expression/primaryExpression/identifier [ <!-- and array is either public or external function argument --> text()[1] = ancestor::functionDefinition[visibleType[matches(text()[1], "^public$|^external$")] or not(visibleType)]//parameter/identifier/text() <!-- or internal or private function argument with memory visibility modifier --> or text()[1] = ancestor::functionDefinition[visibleType[matches(text()[1], "^private$|^internal$")]]//parameter[storageLocation/text() = "memory"]/identifier/text() <!-- or copied to local variable with memory visibility modifier --> or text()[1] = ancestor::functionDefinition//variableDeclaration[storageLocation/text() = "memory"]/identifier/text() ] <!-- may be a variable in condition and it is ... --> or condition/expression[matches(text()[1], "<|<=")]/expression[2]/primaryExpression/identifier [ <!-- new variable with memory array length --> text()[1] = (ancestor::functionDefinition//variableDeclaration[text()[1] = "="] [expression[text()[1] = ".length"]/expression/primaryExpression/identifier [ <!-- and array is either public or external function argument --> text()[1] = ancestor::functionDefinition[visibleType[matches(text()[1], "^public$|^external$")] or not(visibleType)]//parameter/identifier/text() <!-- or internal or private function argument with memory visibility modifier --> or text()[1] = ancestor::functionDefinition[visibleType[matches(text()[1], "^private$|^internal$")]]//parameter[storageLocation/text() = "memory"]/identifier/text() <!-- or copied to local variable with memory visibility modifier --> or text()[1] = ancestor::functionDefinition//variableDeclaration[storageLocation/text() = "memory"]/identifier/text() ] ]/identifier/text()[1]) <!-- or reused variable with memory array length --> or text()[1] = (ancestor::functionDefinition//expression[text()[1] = "="] [expression[2][text()[1] = ".length"]/expression/primaryExpression/identifier [ <!-- and array is either public or external function argument --> text()[1] = ancestor::functionDefinition[visibleType[matches(text()[1], "^public$|^external$")] or not(visibleType)]//parameter/identifier/text() <!-- or internal or private function argument with memory visibility modifier --> or text()[1] = ancestor::functionDefinition[visibleType[matches(text()[1], "^private$|^internal$")]]//parameter[storageLocation/text() = "memory"]/identifier/text() <!-- or copied to local variable with memory visibility modifier --> or text()[1] = ancestor::functionDefinition//variableDeclaration[storageLocation/text() = "memory"]/identifier/text() ] ]/expression[1]//identifier/text()[1]) ] ] ```   ``` forStatement [ ( <!-- either reused variable with .length in pre-condition --> expression[1][text()[1] = "="]/expression[2][text()[1] = ".length"]/expression/primaryExpression/identifier <!-- or new variable with .length in pre-condition --> | expression[1]/variableDeclaration[text()[1] = "="]/expression[text()[1] = ".length"]/expression/primaryExpression/identifier ) <!-- and array is neither public or external function argument --> [not(text()[1] = ancestor::functionDefinition[visibleType[matches(text()[1], "^public$|^external$")] or not(visibleType)]//parameter/identifier/text())] <!-- nor internal or private function argument with memory visibility modifier --> [not(text()[1] = ancestor::functionDefinition[visibleType[matches(text()[1], "^private$|^internal$")]]//parameter[storageLocation/text() = "memory"]/identifier/text())] <!-- nor copied to local variable with memory visibility modifier --> [not(text()[1] = ancestor::functionDefinition//variableDeclaration[storageLocation/text() = "memory"]/identifier/text())] ] [condition/expression[matches(text()[1], ">|>=")]] ```   ``` //forStatement [ ( <!-- either reused variable with .length in pre-condition --> expression[1][text()[1] = "="]/expression[2][text()[1] = ".length"]/expression/primaryExpression/identifier <!-- or new variable with .length in pre-condition --> | expression[1]/variableDeclaration[text()[1] = "="]/expression[text()[1] = ".length"]/expression/primaryExpression/identifier ) [ <!-- and array is either public or external function argument --> text()[1] = ancestor::functionDefinition[visibleType[matches(text()[1], "^public$|^external$")] or not(visibleType)]//parameter/identifier/text() <!-- or internal or private function argument with memory visibility modifier --> or text()[1] = ancestor::functionDefinition[visibleType[matches(text()[1], "^private$|^internal$")]]//parameter[storageLocation/text() = "memory"]/identifier/text() <!-- or copied to local variable with memory visibility modifier --> or text()[1] = ancestor::functionDefinition//variableDeclaration[storageLocation/text() = "memory"]/identifier/text() ] ] [condition/expression[matches(text()[1], ">|>=")]] ```   ``` //whileStatement/condition [ not(descendant-or-self::functionCall) and not(expression/expression/primaryExpression/numberLiteral/decimalNumber) ] ```   ``` //whileStatement[condition/descendant::functionCall] ``` ### Sample Code ``` pragma solidity 0.4.24; contract GasLimitInLoops { function foo() pure internal returns (uint) { return(100); } function testWhile() public { uint x=0; uint[] memory y; // <yes> <report> SOLIDITY_GAS_LIMIT_IN_LOOPS 38f6c7 while (x < foo()) { } while (x > 100) { } // <yes> <report> SOLIDITY_GAS_LIMIT_IN_LOOPS 17f23a while (y[5] < x) { } } function testFor(address[] _addr, uint amount) public { // <yes> <report> SOLIDITY_GAS_LIMIT_IN_LOOPS 4b7do5 for (uint i = 0; i < _addr.length; i++) { } uint n = _addr.length; // <yes> <report> SOLIDITY_GAS_LIMIT_IN_LOOPS 4b7do5 for (i = 0; i < n; i++) { } uint m; m = _addr.length; // <yes> <report> SOLIDITY_GAS_LIMIT_IN_LOOPS 4b7do5 for (i = 0; i < m; i++) { } // <yes> <report> SOLIDITY_GAS_LIMIT_IN_LOOPS 4b7do5 for (uint k; k < _addr.length; k++) { } // <yes> <report> SOLIDITY_GAS_LIMIT_IN_LOOPS v5j3d9 for (i = _addr.length; i > 0; i--) { } // <yes> <report> SOLIDITY_GAS_LIMIT_IN_LOOPS v5j3d9 for (uint j = _addr.length; j > 0; j--) { } } address[] stor; function testForMemory(address[] memory mem) { // <yes> <report> SOLIDITY_GAS_LIMIT_IN_LOOPS 4b7do5 for (uint i = 0; i < mem.length; i++) { } uint n = mem.length; // <yes> <report> SOLIDITY_GAS_LIMIT_IN_LOOPS 4b7do5 for (i = 0; i < n; i++) { } uint m; m = mem.length; // <yes> <report> SOLIDITY_GAS_LIMIT_IN_LOOPS 4b7do5 for (i = 0; i < m; i++) { } // <yes> <report> SOLIDITY_GAS_LIMIT_IN_LOOPS 4b7do5 for (uint k; k < mem.length; k++) { } address[] memory mem2; // <yes> <report> SOLIDITY_GAS_LIMIT_IN_LOOPS 4b7do5 for (i = 0; i < mem2.length; i++) { } uint n2 = mem2.length; // <yes> <report> SOLIDITY_GAS_LIMIT_IN_LOOPS 4b7do5 for (i = 0; i < n2; i++) { } uint m2; m2 = mem2.length; // <yes> <report> SOLIDITY_GAS_LIMIT_IN_LOOPS 4b7do5 for (i = 0; i < m2; i++) { } // <yes> <report> SOLIDITY_GAS_LIMIT_IN_LOOPS 4b7do5 for (k = 0; k < mem2.length; k++) { } // <yes> <report> SOLIDITY_GAS_LIMIT_IN_LOOPS 12cf32 for (uint j = stor.length; j > 0; j--) { } // <yes> <report> SOLIDITY_GAS_LIMIT_IN_LOOPS f6f853 for (k = 0; k < stor.length; k++) { } // <yes> <report> SOLIDITY_GAS_LIMIT_IN_LOOPS v5j3d9 for (i = mem.length; i > 0; i--) { } // <yes> <report> SOLIDITY_GAS_LIMIT_IN_LOOPS v5j3d9 for (j = mem2.length; j > 0; j--) { } } } ``` ### Abstract Syntax Tree [Click Here](https://astexplorer.net/#/gist/48d86c63521f85c514f9bb027b3a03f7/d30bac03e87f5ae246c611966f23bc3f7dcae9e9) to view the AST for the above code. Code generated from AST Explorer using _solidity-parser-antlr-0.4.11_ ### Code Result ``` SOLIDITY_EXTRA_GAS_IN_LOOPS patternId: d3j11j severity: 1 line: 24 column: 8 content: for(uinti=0;i<_addr.length;i++){} ruleId: SOLIDITY_EXTRA_GAS_IN_LOOPS patternId: d3j11j severity: 1 line: 39 column: 8 content: for(uintk;k<_addr.length;k++){} ruleId: SOLIDITY_EXTRA_GAS_IN_LOOPS patternId: d3j11j severity: 1 line: 55 column: 8 content: for(uinti=0;i<mem.length;i++){} ruleId: SOLIDITY_EXTRA_GAS_IN_LOOPS patternId: d3j11j severity: 1 line: 70 column: 8 content: for(uintk;k<mem.length;k++){} ruleId: SOLIDITY_EXTRA_GAS_IN_LOOPS patternId: d3j11j severity: 1 line: 76 column: 8 content: for(i=0;i<mem2.length;i++){} ruleId: SOLIDITY_EXTRA_GAS_IN_LOOPS patternId: d3j11j severity: 1 line: 91 column: 8 content: for(k=0;k<mem2.length;k++){} ruleId: SOLIDITY_EXTRA_GAS_IN_LOOPS patternId: d3j11j severity: 1 line: 99 column: 8 content: for(k=0;k<stor.length;k++){} ruleId: SOLIDITY_GAS_LIMIT_IN_LOOPS patternId: f6f853 severity: 2 line: 24 column: 8 content: for(uinti=0;i<_addr.length;i++){} ruleId: SOLIDITY_GAS_LIMIT_IN_LOOPS patternId: f6f853 severity: 2 line: 29 column: 8 content: for(i=0;i<n;i++){} ruleId: SOLIDITY_GAS_LIMIT_IN_LOOPS patternId: f6f853 severity: 2 line: 35 column: 8 content: for(i=0;i<m;i++){} ruleId: SOLIDITY_GAS_LIMIT_IN_LOOPS patternId: f6f853 severity: 2 line: 39 column: 8 content: for(uintk;k<_addr.length;k++){} ruleId: SOLIDITY_GAS_LIMIT_IN_LOOPS patternId: f6f853 severity: 2 line: 55 column: 8 content: for(uinti=0;i<mem.length;i++){} ruleId: SOLIDITY_GAS_LIMIT_IN_LOOPS patternId: f6f853 severity: 2 line: 60 column: 8 content: for(i=0;i<n;i++){} ruleId: SOLIDITY_GAS_LIMIT_IN_LOOPS patternId: f6f853 severity: 2 line: 66 column: 8 content: for(i=0;i<m;i++){} ruleId: SOLIDITY_GAS_LIMIT_IN_LOOPS patternId: f6f853 severity: 2 line: 70 column: 8 content: for(uintk;k<mem.length;k++){} ruleId: SOLIDITY_GAS_LIMIT_IN_LOOPS patternId: f6f853 severity: 2 line: 76 column: 8 content: for(i=0;i<mem2.length;i++){} ruleId: SOLIDITY_GAS_LIMIT_IN_LOOPS patternId: f6f853 severity: 2 line: 81 column: 8 content: for(i=0;i<n2;i++){} ruleId: SOLIDITY_GAS_LIMIT_IN_LOOPS patternId: f6f853 severity: 2 line: 87 column: 8 content: for(i=0;i<m2;i++){} ruleId: SOLIDITY_GAS_LIMIT_IN_LOOPS patternId: f6f853 severity: 2 line: 91 column: 8 content: for(k=0;k<mem2.length;k++){} ruleId: SOLIDITY_GAS_LIMIT_IN_LOOPS patternId: f6f853 severity: 2 line: 99 column: 8 content: for(k=0;k<stor.length;k++){} ruleId: SOLIDITY_GAS_LIMIT_IN_LOOPS patternId: 12cf32 severity: 2 line: 43 column: 8 content: for(i=_addr.length;i>0;i--){} ruleId: SOLIDITY_GAS_LIMIT_IN_LOOPS patternId: 12cf32 severity: 2 line: 46 column: 8 content: for(uintj=_addr.length;j>0;j--){} ruleId: SOLIDITY_GAS_LIMIT_IN_LOOPS patternId: 12cf32 severity: 2 line: 95 column: 8 content: for(uintj=stor.length;j>0;j--){} ruleId: SOLIDITY_GAS_LIMIT_IN_LOOPS patternId: 12cf32 severity: 2 line: 103 column: 8 content: for(i=mem.length;i>0;i--){} ruleId: SOLIDITY_GAS_LIMIT_IN_LOOPS patternId: 12cf32 severity: 2 line: 107 column: 8 content: for(j=mem2.length;j>0;j--){} ruleId: SOLIDITY_GAS_LIMIT_IN_LOOPS patternId: 17f23a severity: 1 line: 17 column: 15 content: y[5]<x ruleId: SOLIDITY_GAS_LIMIT_IN_LOOPS patternId: 38f6c7 severity: 2 line: 12 column: 8 content: while(x<foo()){} ruleId: SOLIDITY_UPGRADE_TO_050 patternId: 341gim severity: 1 line: 21 column: 21 content: address[]_addr ruleId: SOLIDITY_VISIBILITY patternId: 910067 severity: 1 line: 52 column: 4 content: functiontestForMemory(address[]memorymem){for(uinti=0;i<mem.length;i++){}uintn=mem.length;for(i=0;i<n;i++){}uintm;m=mem.length;for(i=0;i<m;i++){}for(uintk;k<mem.length;k++){}address[]memorymem2;for(i=0;i<mem2.length;i++){}uintn2=mem2.length;for(i=0;i<n2;i++){}uintm2;m2=mem2.length;for(i=0;i<m2;i++){}for(k=0;k<mem2.length;k++){}for(uintj=stor.length;j>0;j--){}for(k=0;k<stor.length;k++){}for(i=mem.length;i>0;i--){}for(j=mem2.length;j>0;j--){}} ruleId: SOLIDITY_VISIBILITY patternId: b51ce0 severity: 1 line: 50 column: 4 content: address[]stor; SOLIDITY_VISIBILITY :2 SOLIDITY_EXTRA_GAS_IN_LOOPS :7 SOLIDITY_UPGRADE_TO_050 :1 SOLIDITY_GAS_LIMIT_IN_LOOPS :20 ```