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

0 stars 0 forks source link

Improper error handling #219

Closed c4-bot-6 closed 1 month ago

c4-bot-6 commented 1 month ago

Lines of code

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

Vulnerability details

Impact Improper error handling in critical functions like rebalance can lead to unexpected contract behavior, including failed trades, unhandled exceptions, and potential loss of funds. This can compromise the stability and reliability of the RToken system, potentially leading to undercollateralization, loss of user confidence, and financial loss. The use of generic error handling without informative error messages or detailed exception management can make it difficult to diagnose and resolve issues, increasing the risk of systemic failures.

Proof of Concept Code Reference: The rebalance function contains several critical steps that are susceptible to errors, including external calls, conditional checks, and trading operations:

solidity Copy code function rebalance(TradeKind kind) external nonReentrant { requireNotTradingPausedOrFrozen(); assetRegistry.refresh();

// DoS prevention:
// unless caller is self, require that the next auction is not in same block
require(
    _msgSender() == address(this) || tradeEnd[kind] < block.timestamp,
    "already rebalancing"
);

require(tradesOpen == 0, "trade open");
require(basketHandler.isReady(), "basket not ready");
require(block.timestamp >= basketHandler.timestamp() + tradingDelay, "trading delayed");

BasketRange memory basketsHeld = basketHandler.basketsHeldBy(address(this));
require(basketsHeld.bottom < rToken.basketsNeeded(), "already collateralized");

// Dissolve any held RToken balance if above threshold
uint256 balance = rToken.balanceOf(address(this));
if (balance >= MAX_DISTRIBUTION * MAX_DESTINATIONS) rToken.dissolve(balance);
if (basketsHeld.bottom >= rToken.basketsNeeded()) return; // return if now capitalized

// Recollateralization process
(TradingContext memory ctx, Registry memory reg) = tradingContext(basketsHeld);
(bool doTrade, TradeRequest memory req, TradePrices memory prices) = RecollateralizationLibP1.prepareRecollateralizationTrade(ctx, reg);

if (doTrade) {
    IERC20 sellERC20 = req.sell.erc20();

    // Seize RSR if needed
    if (sellERC20 == rsr) {
        uint256 bal = sellERC20.balanceOf(address(this));
        if (req.sellAmount > bal) stRSR.seizeRSR(req.sellAmount - bal);
    }

    // Execute Trade
    try this.tryTrade(kind, req, prices) returns (ITrade trade) {
        tradeEnd[kind] = trade.endTime(); // {s}
        tokensOut[sellERC20] = trade.sellAmount(); // {tok}
    } catch (bytes memory errData) {
        if (errData.length == 0) {
            revert("Trade execution failed without error message"); // Provide a detailed revert message
        } else {
            revert(string(abi.encodePacked("Trade execution failed: ", errData))); // Decode and provide error details
        }
    }
} else {
    // Haircut time
    compromiseBasketsNeeded(basketsHeld.bottom);
}

} Potential Issues: Unclear Error Messages: The current error handling with revert() lacks detailed error messages. This makes it difficult to debug and diagnose issues when trades fail. For example, if a trade fails during execution and the exception is caught, the lack of an informative error message (revert(); // solhint-disable-line reason-string) can obscure the underlying problem.

Unhandled Exceptions: Catching exceptions with a try/catch block but not handling the caught exceptions adequately (e.g., simply reverting without any additional logic or logging) can mask the root causes of errors, making it challenging to understand and fix the issues. It may also prevent the contract from gracefully handling the error or reverting to a safe state.

Screenshot/Logs: N/A (Conceptual explanation based on code logic).

Tools Used Manual code review Solidity documentation Recommended Mitigation Steps Detailed Error Messages: Use descriptive error messages in revert() statements to aid in debugging. This can be achieved by including a string message that clearly indicates the reason for the failure.

For example:

solidity Copy code if (errData.length == 0) { revert("Trade execution failed without error message"); } else { revert(string(abi.encodePacked("Trade execution failed: ", errData))); }

Assessed type

Error