code-423n4 / 2024-07-reserve-validation

0 stars 0 forks source link

Zero Distribution Vulnerability in Revenue Distribution Logic #102

Closed c4-bot-8 closed 1 month ago

c4-bot-8 commented 2 months ago

Lines of code

https://github.com/code-423n4/2024-07-reserve/blob/3f133997e186465f4904553b0f8e86ecb7bbacbf/contracts/p1/Distributor.sol#L120-L200

Vulnerability details

Impact

The vulnerability can cause the distribute function to revert if totalShares is zero. This can halt the distribution process, leading to a denial of service for revenue distribution. It can prevent the contract from functioning as intended, affecting all stakeholders relying on the distribution mechanism. if (totalShares != 0) tokensPerShare = amount / totalShares;

Proof of Concept

  1. Setup:
    • Deploy the DistributorP1 contract.
    • Ensure no distributions are set or all distributions have zero values.
  2. Trigger the Bug:
    • Call the distribute function with any amount of rsr or rToken.
      
      // Deploy the contract
      DistributorP1 distributor = new DistributorP1();
      IERC20 rsr = IERC20(address(0x...)); // RSR token address
      IERC20 rToken = IERC20(address(0x...)); // RToken address

// Initialize the contract with zero distributions distributor.init(main, RevenueShare(0, 0));

// Attempt to distribute tokens distributor.distribute(rsr, 1000); // This will revert due to zero totalShares


## Tools Used
Manual review

## Recommended Mitigation Steps
Ensure `totalShares` is not zero before performing the division.

function distribute(IERC20 erc20, uint256 amount) external { // Intentionally do not check notTradingPausedOrFrozen, since handled by caller

address caller = _msgSender();
require(caller == rsrTrader || caller == rTokenTrader, "RevenueTraders only");
require(erc20 == rsr || erc20 == rToken, "RSR or RToken");
bool isRSR = erc20 == rsr; // if false: isRToken

uint256 tokensPerShare;
uint256 totalShares;
{
    RevenueTotals memory revTotals = totals();
    totalShares = isRSR ? revTotals.rsrTotal : revTotals.rTokenTotal;
    require(totalShares != 0, "totalShares cannot be zero"); // Tambahkan pengecekan ini
    tokensPerShare = amount / totalShares;
    require(tokensPerShare != 0, "nothing to distribute");
}
// Evenly distribute revenue tokens per distribution share.
// This rounds "early", and that's deliberate!

Transfer[] memory transfers = new Transfer[](destinations.length());
uint256 numTransfers;

bool accountRewards = false;
uint256 paidOutShares;

for (uint256 i = 0; i < destinations.length(); ++i) {
    address addrTo = destinations.at(i);

    uint256 numberOfShares = isRSR
        ? distribution[addrTo].rsrDist
        : distribution[addrTo].rTokenDist;
    if (numberOfShares == 0) continue;
    uint256 transferAmt = tokensPerShare * numberOfShares;
    paidOutShares += numberOfShares;

    if (addrTo == FURNACE) {
       addrTo = address(furnace);
        if (transferAmt != 0) accountRewards = true;
    } else if (addrTo == ST_RSR) {
        addrTo = address(stRSR);
        if (transferAmt != 0) accountRewards = true;
    }

    transfers[numTransfers] = Transfer({ addrTo: addrTo, amount: transferAmt });
    ++numTransfers;
}
emit RevenueDistributed(erc20, caller, amount);

// == Interactions ==
for (uint256 i = 0; i < numTransfers; ++i) {
    IERC20Upgradeable(address(erc20)).safeTransferFrom(
        caller,
        transfers[i].addrTo,
        transfers[i].amount
    );
}

DAOFeeRegistry daoFeeRegistry = main.daoFeeRegistry();
if (address(daoFeeRegistry) != address(0)) {
    if (totalShares > paidOutShares) {
        (address recipient, , ) = main.daoFeeRegistry().getFeeDetails(address(rToken));

        if (recipient != address(0)) {
            IERC20Upgradeable(address(erc20)).safeTransferFrom(
                caller,
                recipient,
                tokensPerShare * (totalShares - paidOutShares)
            );
        }
    }
}

// Perform reward accounting
if (accountRewards) {
    if (isRSR) {
        stRSR.payoutRewards();
    } else {
        furnace.melt();
    }
}

}



## Assessed type

Math