Lucene search

K
code423n4Code4renaCODE423N4:2023-08-POOLTOGETHER-FINDINGS-ISSUES-143
HistoryAug 07, 2023 - 12:00 a.m.

It is possible that function rngComplete() does not iterate through all rewards

2023-08-0700:00:00
Code4rena
github.com
4
rng relay auction
vulnerability
impact
proof of concept
data type
mitigation
loop

Lines of code

Vulnerability details

Impact

In RngRelayAuction.sol we have rngComplete():

function rngComplete(
    uint256 _randomNumber,
    uint256 _rngCompletedAt,
    address _rewardRecipient,
    uint32 _sequenceId,
    AuctionResult calldata _rngAuctionResult
  ) external returns (bytes32) {
    if (_sequenceHasCompleted(_sequenceId)) revert SequenceAlreadyCompleted();
    uint64 _auctionElapsedSeconds = uint64(block.timestamp < _rngCompletedAt ? 0 : block.timestamp - _rngCompletedAt);
    if (_auctionElapsedSeconds > (_auctionDurationSeconds-1)) revert AuctionExpired();
    // Calculate the reward fraction and set the draw auction results
    UD2x18 rewardFraction = _fractionalReward(_auctionElapsedSeconds);
    _auctionResults.rewardFraction = rewardFraction;
    _auctionResults.recipient = _rewardRecipient;
    _lastSequenceId = _sequenceId;

    AuctionResult[] memory auctionResults = new AuctionResult[](2);
    auctionResults[0] = _rngAuctionResult;
    auctionResults[1] = AuctionResult({
      rewardFraction: rewardFraction,
      recipient: _rewardRecipient
    });

    uint32 drawId = prizePool.closeDraw(_randomNumber);

    uint256 futureReserve = prizePool.reserve() + prizePool.reserveForOpenDraw();
    uint256[] memory _rewards = RewardLib.rewards(auctionResults, futureReserve);

    emit RngSequenceCompleted(
      _sequenceId,
      drawId,
      _rewardRecipient,
      _auctionElapsedSeconds,
      rewardFraction
    );

    for (uint8 i = 0; i < _rewards.length; i++) {
      uint104 _reward = uint104(_rewards[i]);
      if (_reward > 0) {
        prizePool.withdrawReserve(auctionResults[i].recipient, _reward);
        emit AuctionRewardDistributed(_sequenceId, auctionResults[i].recipient, i, _reward);
      }
    }

    return bytes32(uint(drawId));
  }

This function handles the completion of an RNG relay auction. The problem is this block of code:

for (uint8 i = 0; i < _rewards.length; i++) {
      uint104 _reward = uint104(_rewards[i]);
      if (_reward > 0) {
        prizePool.withdrawReserve(auctionResults[i].recipient, _reward);
        emit AuctionRewardDistributed(_sequenceId, auctionResults[i].recipient, i, _reward);
      }
    }

The variable i is declared as a uint8, which means it can only hold values from 0 to 255. If _rewards.length were to be more than 255, the function will revert.

Proof of Concept

The issue lies in the use of uint8 as the loop iterator, which can only represent values between 0 to 255. If the length of the _rewards array exceeds 255, the loop will revert.

In rngComplete() we see the _rewards is uint256:

157: uint256[] memory _rewards = RewardLib.rewards(auctionResults, futureReserve);

After this in the for loop i is uint8:

for (uint8 i = 0; i < _rewards.length; i++) {
      uint104 _reward = uint104(_rewards[i]);
      if (_reward > 0) {
        prizePool.withdrawReserve(auctionResults[i].recipient, _reward);
        emit AuctionRewardDistributed(_sequenceId, auctionResults[i].recipient, i, _reward);
      }
    }

But _rewards.length can be bigger than uint8. And if so the function will revert.

Tools Used

Visual Studio Code

Recommended Mitigation Steps

I recommend changing the data type of the iterator from uint8 to uint256, as these data types can handle a much larger range of values. By making this change, the functions would be able to process any number of rewards.

Assessed type

Loop


The text was updated successfully, but these errors were encountered:

All reactions