Summary
External call in unbounded loop of consolidatePendingStakes
function can result in a DoS.
Vulnerability Details
If the pendingStakes
array grows too large, it will cause consolidatePendingStakes
function to run out of gas and revert.
This is because of consolidatePendingStakes
calls deletePendingStake
which assign a new index value inside it through a loop which is later deleted and clearly increases the gas cost and have high possibility of running out of gas.
function consolidatePendingStakes() private {
uint256 deadline = block.timestamp - 1 days;
for (int256 i = 0; uint256(i) < pendingStakes.length; i++) {
PendingStake memory _stake = pendingStakes[uint256(i)];
if (_stake.createdAt < deadline) {
positions[_stake.holder].holder = _stake.holder;
positions[_stake.holder].TST += _stake.TST;
positions[_stake.holder].EUROs += _stake.EUROs;
deletePendingStake(uint256(i));
i--;
}
}
}
Impact
Consider limiting the number of iterations in loops that make unbounded calls.
Tools Used
Manual code review
Recommendations
Add a maximum size limit for array in consolidatePendingStakes
.
function consolidatePendingStakes() private {
uint256 deadline = block.timestamp - 1 days;
for (int256 i = 0; uint256(i) < pendingStakes.length; i++) {
+ if (i == 100){
+ revert;
+ }
PendingStake memory _stake = pendingStakes[uint256(i)];
if (_stake.createdAt < deadline) {
positions[_stake.holder].holder = _stake.holder;
positions[_stake.holder].TST += _stake.TST;
positions[_stake.holder].EUROs += _stake.EUROs;
deletePendingStake(uint256(i));
// pause iterating on loop because there has been a deletion. "next" item has same index
i--;
}
}
}