DeFiFoundry
20,000 USDC
View results
Submission Details
Severity: medium
Invalid

Overflow in FjordStaking.sol::_checkEpochRollover()

Summary

Overflow while calculating pendingRewardsPerToken variable in Overflow in FjordStaking.sol::_checkEpochRollover()

Vulnerability Details

https://github.com/Cyfrin/2024-08-fjord/blob/main/src/FjordStaking.sol#L691
Possibility of overflow/revert when calling the auctionEnd function.
The pendingRewardsPerToken variable may overflow when multiplying pendingRewards with PRECISION_18 if pendingRewards is very high.

Explainations

In Solidity 0.8.21, overflows are automatically detected, and the operation will fail (revert).

In the _checkEpochRollover() function, the max limit of pendingRewards is the max of uint256.
However, there is a risk of overflow on this line of code:

uint256 pendingRewardsPerToken = (pendingRewards * PRECISION_18) / totalStaked;

Impact

The _checkEpochRollover function cannot be used, as it will revert on each call.
As the _checkEpochRollover function is used in the checkEpochRollover modifier, all the fonctions using this modifier will revert.
So the stake(), stakeVested(), unstake(), unstakeVested(), unstakeAll(), claimReward(), completeClaimRequest(), addReward() and the onStreamCanceled() will revert.

Tools Used

Manual verification

Recommendations

https://github.com/Cyfrin/2024-08-fjord/blob/main/src/FjordStaking.sol#L691
Use a condition to check that pendingRewards is not too high when calculating pendingRewardsPerToken.

function _checkEpochRollover() internal {
uint16 latestEpoch = getEpoch(block.timestamp);
if (latestEpoch > currentEpoch) {
//Time to rollover
currentEpoch = latestEpoch;
if (totalStaked > 0) {
uint256 currentBalance = fjordToken.balanceOf(address(this));
// no distribute the rewards to the users coming in the current epoch
uint256 pendingRewards = (currentBalance +
totalVestedStaked +
newVestedStaked) -
totalStaked -
newStaked -
totalRewards;
///MODIFICATION HERE///
if (pendingRewards >= 1.1579e59) {
revert("pendingRewards is to high");
}
///END OF THE MODIFICATION///
uint256 pendingRewardsPerToken = (pendingRewards *
PRECISION_18) / totalStaked;
totalRewards += pendingRewards;
for (uint16 i = lastEpochRewarded + 1; i < currentEpoch; i++) {
rewardPerToken[i] =
rewardPerToken[lastEpochRewarded] +
pendingRewardsPerToken;
emit RewardPerTokenChanged(i, rewardPerToken[i]);
}
} else {
for (uint16 i = lastEpochRewarded + 1; i < currentEpoch; i++) {
rewardPerToken[i] = rewardPerToken[lastEpochRewarded];
emit RewardPerTokenChanged(i, rewardPerToken[i]);
}
}
totalStaked += newStaked;
totalVestedStaked += newVestedStaked;
newStaked = 0;
newVestedStaked = 0;
lastEpochRewarded = currentEpoch - 1;
}
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.