The High Cost of Unbounded Loops: Denial of Service
Unbounded Loop Vulnerability:
An unbounded loop vulnerability is a type of security flaw that can occur in smart contracts when a loop does not have a defined maximum iteration limit. This means that the loop can continue to run indefinitely, potentially consuming all of the gas allocated to the contract and rendering it unresponsive.
For example, consider the following code:
In this example, the infiniteLoop function has a while loop with a condition of "true", which means it will run indefinitely. In a smart contract, this can cause the contract to consume all of the gas allocated to it and become unresponsive. This could lead to funds being locked in the contract, or the contract being unable to process any further transactions.
Let’s understand more clearly with a relatable example here,
Imagine you set a timer to limit your break time while studying for an exam. But due to a malfunction, the timer runs indefinitely and you end up wasting hours watching videos instead of studying.
Anyways, this is an example of an unbounded loop vulnerability, where a lack of a maximum limit causes you to consume valuable resources, in this case, your time, and become unprepared for your exam. It is important to set limits and monitor them to avoid this kind of vulnerability.
Let’s dig more about gas limit:
Gas Limit: In the Ethereum network, every time a smart contract is executed, it consumes a certain amount of computational resources, measured in gas. Gas limit is a mechanism that helps to prevent infinite loops and other unintended computations from consuming all of the network's resources. It sets a maximum limit on the amount of gas that can be used to execute a smart contract.
How gas limit helps in solving the halting problem:
When the amount of gas used by a contract exceeds the gas limit, the contract's execution is halted, and any changes made to the blockchain are rolled back. This ensures that no single contract can consume an excessive amount of resources and cause a halting problem.
Think of it like a fuel tank for a car, the gas limit is the maximum amount of fuel that can be used by the car, once the fuel runs out, the car stops running and so does the contract. This mechanism helps in keeping the network running smoothly and it prevents any infinite loops or unexpected computations that could cause problems in the network.
The code we are gonna look at :
- The Project contract has an unbounded loop vulnerability
- The vulnerability is caused by the loop not having a maximum limit for the number of iterations
- If the
lengthvariable is set to a very large number, the loop could run indefinitely
- This can cause the contract to run out of gas, which would prevent it from completing its intended function
- This can lead to a loss of resources, such as gas, and a failure of the contract to function as intended. i.e. halted 😵
Attacker senerio👀 :
- An attacker calls the "addTasks" function to add a large number of tasks to the contract.
- The function does not have any restriction on the number of tasks that can be added, so the attacker can add a large number of tasks.
- When the "lendToProject" function is called, it calls the "projectCost" function to calculate the total cost of the tasks.
- The "projectCost" function iterates through all tasks to calculate the total cost, regardless of the number of tasks.
- As the number of tasks added by the attacker is large, the iteration takes a lot of computational resources and gas.
- As a result, the "projectCost" function exceeds the block gas limit, and the transaction is rolled back.
- This can cause a denial-of-service (DoS) attack as the legitimate users will not be able to call the "lendToProject" function.
- The attacker could also leverage this vulnerability to increase the cost of gas required to execute the contract, making it more expensive for other users to interact with it
In conclusion, unbounded loop vulnerabilities can have serious consequences for smart contracts, such as causing a denial of service attack or running out of gas. To prevent these issues, it is important for developers to ensure that any loops in their contracts have a defined maximum limit and to use more efficient algorithms where possible. Additionally, implementing a gas limit can act as a safety mechanism to prevent infinite loops and unintended computations from consuming all of the network's resources.
The mentioned code is a medium-risk finding from a code4rena audit
Here is more information and findings from the Rigor Contest 2022-08-rigour-findings
Thank you for reading!