stake.link

stake.link
DeFiHardhatBridge
27,500 USDC
View results
Submission Details
Severity: medium
Invalid

`effectiveBalanceOf` in `SDLPool.sol` uses unchecked addition, risking integer overflow. In `SDLPoolCCIPControllerPrimary.sol`, an overflow exploit can claim all protocol rewards.

Summary

effectiveBalanceOf, do arithmetic without checking for overflows which could lead to unexpected behavior. This unchecked addition could be vulnerable to an integer overflow. For example, if a user has the max uint256 amount of base SDL balance, adding even 1 wei of bonus could overflow and wrap around to 0.

Vulnerability Details

The effectiveBalanceOf function in SDLPool.sol performs an unchecked addition when calculating a user's effective balance: SDLPool.sol#effectiveBalanceOf

function effectiveBalanceOf(address _account) external view returns (uint256) {
return effectiveBalances[_account];
}

Here, effectiveBalances is a mapping that stores the sum of a user's base SDL balance + any applicable locking bonuses.

Impact

In distributeRewards of SDLPoolCCIPControllerPrimary.sol, rewards are divided proportionally based on each user's effective balance percentage of the total effective supply. SDLPoolCCIPControllerPrimary.sol#distributeRewards

function distributeRewards() external onlyRewardsInitiator {
uint256 totalRESDL = ISDLPoolPrimary(sdlPool).effectiveBalanceOf(address(this));
address[] memory tokens = ISDLPoolPrimary(sdlPool).supportedTokens();
uint256 numDestinations = whitelistedChains.length;
ISDLPoolPrimary(sdlPool).withdrawRewards(tokens);
uint256[][] memory distributionAmounts = new uint256[][](numDestinations);
for (uint256 i = 0; i < numDestinations; ++i) {
distributionAmounts[i] = new uint256[](tokens.length);
}
for (uint256 i = 0; i < tokens.length; ++i) {
address token = tokens[i];
uint256 tokenBalance = IERC20(token).balanceOf(address(this));
address wrappedToken = wrappedRewardTokens[token];
if (wrappedToken != address(0)) {
IERC677(token).transferAndCall(wrappedToken, tokenBalance, "");
tokens[i] = wrappedToken;
tokenBalance = IERC20(wrappedToken).balanceOf(address(this));
}
uint256 totalDistributed;
for (uint256 j = 0; j < numDestinations; ++j) {
uint64 chainSelector = whitelistedChains[j];
uint256 rewards = j == numDestinations - 1
? tokenBalance - totalDistributed
: (tokenBalance * reSDLSupplyByChain[chainSelector]) / totalRESDL;
distributionAmounts[j][i] = rewards;
totalDistributed += rewards;
}
}
for (uint256 i = 0; i < numDestinations; ++i) {
_distributeRewards(whitelistedChains[i], tokens, distributionAmounts[i]);
}
}

By triggering overflow, exploit contract can manipulate its effective balance to incorrectly show 0, even though it owns practically the entire effective supply. This could allow the exploit contract to claim all protocol rewards.

Tools Used

Manual Review

Recommendations

By using OpenZeppelin's SafeMath libraries to perform checked arithmetic operations.

function effectiveBalanceOf(address _account) external view returns (uint256) {
- return effectiveBalances[_account];
+ return effectiveBalances[_account].add(locks[_account].bonusAmount);
}

Adopting overflow-safe math would mitigate this attack vector.

Updates

Lead Judging Commences

0kage Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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