A malicious user can monitor the mempool for when the distributeRewards() function in SDLPoolPrimaryController.sol is called and can front run that call by staking tokens with a 0 locking duration right before the call to distributeRewards() is processed and then back-running by withdrawing his tokens, all in the same block. A user can theoretically do this every time distributeRewards() is called without ever being in the pool longer than one block.
This steals rewards from people who remain in the pool long term and defeats the purpose of the protocol. This is possible because you aren't required to lock your tokens to still get some of the rewards and because rewards begin to accrue right away.
Note: some of the functions discussed are in RewardsPool.sol which is not directly in scope, but several of the contracts that are in scope (e.g., the SDLPool contracts) inherit from RewardsPoolController.sol which, in its _updateRewards() function, calls RewardsPool.sol and those in-scope contracts use the updateRewards() modifier which ultimately calls functions in RewardsPool.sol
This is the _distributeRewards() function in SDLPoolCCIPControllerPrimary.sol which builds a CCIP message that will send the rewards to the appropriate rewards pool on the applicable chain.
The distributeRewards() function in RewardsPool.sol is called when rewards are distributed and it in turn calls _updateRewardsPerToken() which increments the additional rewards to the rewardPerToken variable
The updateReward() function will be called when the malicious user stakes their tokens before the reward distribution is processed, and it will record his userRewardPerTokenPaid as the existing rewardPerToken (right before rewardPerToken is increased by the call to distributeRewards()).
The malicious user's rewards are given by the withdrawableRewards() function and they will equal his effective balance (amount staked) times (the now increased rewardPerToken - userRewardPerTokenPaid). His userRewardPerTokenPaid will equal the rewardPerToken before the distribution and rewardPerToken will be larger than that thanks to the new distribution (effected by _updateRewardPerToken()) that happened in the same block right after he staked. He can withdraw same block.
The impact is that the people in the pool long term don't get as many rewards as they should, and the protocol is used in a way that it is not intended to be used. Plus, someone could potentially use a flash loan to do this so they could actually take a very disproportionate amount of the rewards by making their stake very big.
Manual review
Require people to stake for a period of time before rewards begin to accrue. Another thing you could do is incorporate time staking into rewards such that you don't have rewardsPerToken in RewardsPool.sol but rewardsPerTokenPerSecond
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.