Normal Behavior:
The pool owner can update the allowed scope of BattleChain accounts using the setPoolScope function before the scope is locked. This function calls _replaceScope to clear the old accounts and add the new ones.
Specific Issue/Problem:
The _replaceScope function uses two unbounded for loops. The first loop iterates over the existing _scopeAccounts to set their mappings to false. The second loop iterates over the new accounts array and makes an external call (IAgreement.isContractInScope) for every single address. Because there is no upper limit on accounts.length, adding a massive array will exceed the block gas limit (OOG).
Even worse, if the owner successfully adds a large array that barely fits the block limit, any future calls to setPoolScope (even with a small array) will revert because the first loop clearing the old array will now hit the block gas limit, permanently locking the scope early.
Likelihood:
Reason 1 // The protocol sponsor/owner attempts to whitelist a large number of BattleChain accounts at once for a massive pool.
Reason 2 // There is no upper limit enforced on the length of the accounts array to prevent gas limit exhaustion.
Impact:
Impact 1 // The setPoolScope function becomes permanently bricked (Denial of Service) due to the Out-of-Gas error on the first loop.
Impact 2 // The pool sponsor permanently loses the ability to modify the pool scope even before scopeLocked becomes true.
Explanation:
The provided Proof of Concept (PoC) below demonstrates how the lack of array length validation can permanently brick the pool scope functionality. In the first step, the pool owner submits a massive array of 3,000 addresses. Due to the external calls inside the loop, this will likely hit the block gas limit and fail. However, if it somehow succeeds, the state becomes severely bloated. In the subsequent step, if the owner tries to update the scope again with just 1 address, the transaction will definitively revert with an Out-of-Gas (OOG) error because the first loop must iterate 3,000 times just to clear the previous state. This permanently locks the owner out of using setPoolScope.
Explanation:
To mitigate this Denial of Service (DoS) vulnerability, the protocol must enforce a strict upper limit on the size of the accounts array passed to the function. By introducing a maximum length check (e.g., maximum 200 or 500 addresses per call, depending on the protocol's expected operational needs), we can mathematically guarantee that the for loops and the external calls will never exceed the block gas limit. This simple bounding check ensures the function remains operational at all times and prevents the contract from being accidentally bricked.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.