Controlled Array Length Remediation
Overview
Related Detector: Controlled Array Length
When any caller can push to a storage array and some function iterates the whole array in one transaction, the gas cost grows with the array. Once it is large enough, the loop exceeds the block gas limit and the function becomes permanently uncallable — a denial of service that can freeze withdrawals or admin actions. The remediation is to cap growth, paginate iteration, or replace linear scans with mappings.
Recommended Fix
Before (Vulnerable)
address[] public participants;
function join() external {
participants.push(msg.sender); // anyone can grow the array without limit
}
function distribute() external {
for (uint256 i; i < participants.length; ++i) {
_reward(participants[i]); // reverts once the array is large enough
}
}
After (Fixed — bounded growth + pull, or paginated processing)
uint256 public constant MAX_PARTICIPANTS = 1000;
address[] public participants;
function join() external {
require(participants.length < MAX_PARTICIPANTS, "Full");
participants.push(msg.sender);
}
// Process a bounded slice per call so no single transaction is unbounded.
function distribute(uint256 start, uint256 count) external {
uint256 end = start + count;
if (end > participants.length) end = participants.length;
for (uint256 i = start; i < end; ++i) {
_reward(participants[i]);
}
}
Where possible, prefer a mapping with O(1) access and a pull-payment claim so no full-array loop is ever required.
Common Mistakes
- Capping the array but still iterating the whole thing elsewhere (e.g. a
viewused on-chain), reintroducing the limit problem. - Choosing a cap so high the loop can still exceed the block gas limit at scale.
- Deleting elements by shifting the array in place, which is itself an O(n) loop with the same failure mode.