Summary
The loop runs forever on LiquidationPool::consolidatePendingStakes function which supposed to end once all the pendingStakers are validated.
Vulnerability Details
The issue arises due to the use of i-- , which reduces the current index by 1, but the next index will again read the same pendingStakes value which will again caause this loop to run until the gas error gets thrown.
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--;
}
}
}
POC => Run the below function in any foundry test file, it just prints 0, -1, 0, -1 for 5 runs.
function test1() external {
uint runs = 0;
for (int256 i = 0; uint256(i) < 2; i++) {
console.logInt(i);
i--;
console.logInt(i);
runs++;
if (runs == 5) break;
}
}
Impact
HIGH - Unable to convert pending satkes to holders.
Tools Used
Manual review
Recommendations
function consolidatePendingStakes() private {
uint256 deadline = block.timestamp - 1 days;
- for (int256 i = 0; uint256(i) < pendingStakes.length; i++) {
+ for (uint256 i = 0; 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));
// pause iterating on loop because there has been a deletion. "next" item has same index
- i--;
+ i++;
}
}
}