Omniscia Xcaliswap Audit

Gauge Manual Review Findings

Gauge Manual Review Findings

GEG-01M: Potential Denial-of-Service Attack of Rewards

TypeSeverityLocation
Logical FaultGauge.sol:L548-L551

Description:

The notifyRewardAmount mechanism will automatically add a token if it does not already exist in the rewards array without an upper limit on the total entries thus potentially creating a Denial-of-Service (DoS) attack on the _updateRewardForAllTokens function.

Impact:

It is currently possible to add an unlimited number of reward tokens which could cause the contract to become inoperable wherever the _updateRewardForAllTokens function is invoked, a trait highly undesirable for such a high-value system.

Example:

contracts/periphery/Gauge.sol
527function notifyRewardAmount(address token, uint amount) external lock {
528 require(token != stake);
529 require(amount > 0);
530 if (rewardRate[token] == 0) _writeRewardPerTokenCheckpoint(token, 0, block.timestamp);
531 (rewardPerTokenStored[token], lastUpdateTime[token]) = _updateRewardPerToken(token, type(uint).max, true);
532 _claimFees();
533
534 if (block.timestamp >= periodFinish[token]) {
535 _safeTransferFrom(token, msg.sender, address(this), amount);
536 rewardRate[token] = amount / DURATION;
537 } else {
538 uint _remaining = periodFinish[token] - block.timestamp;
539 uint _left = _remaining * rewardRate[token];
540 require(amount > _left);
541 _safeTransferFrom(token, msg.sender, address(this), amount);
542 rewardRate[token] = (amount + _left) / DURATION;
543 }
544 require(rewardRate[token] > 0);
545 uint balance = IERC20(token).balanceOf(address(this));
546 require(rewardRate[token] <= balance / DURATION, "Provided reward too high");
547 periodFinish[token] = block.timestamp + DURATION;
548 if (!isReward[token]) {
549 isReward[token] = true;
550 rewards.push(token);
551 }
552
553 emit NotifyReward(msg.sender, token, amount);
554}

Recommendation:

We advise the reward tokens to be specified during the initialization of the contract with a maximum upper limit imposed via a contract-level constant to ensure no malicious tokens can be added to the rewards array and that all the expected reward tokens are defined during the creation of the contract rather than dynamically. Alternatively, we advise a dedicated and privileged function to be introduced that permits manually white-listing reward tokens that would then be accepted by the notifyRewardAmount function. Either of the two solutions are sufficient to deal with the vulnerability described.

Alleviation:

The Xcaliswap team has implemented a constant MAX_REWARD_TOKENS upper bound on the rewards array. Please note, since the require statement uses < operator for upper bound comparison the rewards array can only contain a maximum of 15 values.