code-423n4 / 2023-05-maia-findings

24 stars 13 forks source link

Reentrancy Vulnerability: The contract inherits from the ReentrancyGuard contract, which smay be vulnerable to reentrancy attacks if not properly handled in the contract's logic. #825

Closed code423n4 closed 1 year ago

code423n4 commented 1 year ago

Lines of code

https://github.com/code-423n4/2023-05-maia/blob/cfed0dfa3bebdac0993b1b42239b4944eb0b196c/src/erc-20/ERC20Gauges.sol#L202-L220

Vulnerability details

Impact

The impact of the reentrancy vulnerability in the _incrementGaugeWeight function can be summarized as follows:

Proof of Concept

The part of the code that may be vulnerable to reentrancy attacks is the _incrementGaugeWeight function: LINK

function _incrementGaugeWeight(address user, address gauge, uint112 weight, uint32 cycle) internal {
        if (!_gauges.contains(gauge) || _deprecatedGauges.contains(gauge)) revert InvalidGaugeError();
        unchecked {
            if (cycle - block.timestamp <= incrementFreezeWindow) revert IncrementFreezeError();
        }

        IBaseV2Gauge(gauge).accrueBribes(user);

        bool added = _userGauges[user].add(gauge); // idempotent add
        if (added && _userGauges[user].length() > maxGauges && !canContractExceedMaxGauges[user]) {
            revert MaxGaugeError();
        }

        getUserGaugeWeight[user][gauge] += weight;

        _writeGaugeWeight(_getGaugeWeight[gauge], _add112, weight, cycle);

        emit IncrementGaugeWeight(user, gauge, weight, cycle);
    }

The potential reentrancy vulnerability arises from the accrueBribes function call to the IBaseV2Gauge contract. If the accrueBribes function performs an external call to an untrusted contract before the state changes are completed in the _incrementGaugeWeight function, it can allow the called contract to reenter the _incrementGaugeWeight function and potentially manipulate the contract state in unexpected ways.

Tools Used

Manual Review

Recommended Mitigation Steps

To fix the reentrancy vulnerability in the _incrementGaugeWeight function, we can apply the following measures:

Here's an example of how the fix could be implemented:

bool private _isIncrementingGaugeWeight;

function _incrementGaugeWeight(address user, address gauge, uint112 weight, uint32 cycle) internal {
    require(!_isIncrementingGaugeWeight, "Function locked during execution");
    _isIncrementingGaugeWeight = true;

    if (!_gauges.contains(gauge) || _deprecatedGauges.contains(gauge)) revert InvalidGaugeError();
    unchecked {
        if (cycle - block.timestamp <= incrementFreezeWindow) revert IncrementFreezeError();
    }

    // Move external call to the beginning
    IBaseV2Gauge(gauge).accrueBribes(user);

    bool added = _userGauges[user].add(gauge);
    if (added && _userGauges[user].length() > maxGauges && !canContractExceedMaxGauges[user]) {
        revert MaxGaugeError();
    }

    getUserGaugeWeight[user][gauge] += weight;
    _writeGaugeWeight(_getGaugeWeight[gauge], _add112, weight, cycle);

    _isIncrementingGaugeWeight = false; // Reset the flag
    emit IncrementGaugeWeight(user, gauge, weight, cycle);
}

By applying these fixes, we can mitigate the reentrancy vulnerability and ensure that the contract functions as intended, without being susceptible to reentrant attacks.

Assessed type

Reentrancy

c4-judge commented 1 year ago

trust1995 marked the issue as unsatisfactory: Insufficient proof