Skip to content
This repository has been archived by the owner on Dec 17, 2023. It is now read-only.

Latest commit

 

History

History
50 lines (36 loc) · 3.19 KB

115.md

File metadata and controls

50 lines (36 loc) · 3.19 KB

lil.eth

high

DOS will be caused by Excessive Gas Consumption Vulnerability in _getAccountLiquidity() Function

Summary

The _getAccountLiquidity() function, which uses a for-loop to iterate over all markets entered by a user, exhausts more gas as the number of markets increases. This can lead to a situation where a user with a high number of entered markets exceeds the block gas limit during the redemption process, making the redemption impossible. As currently there is no mechanism implement to ensure this can't happen I believe it must be outlined for the security of this protocol.

Vulnerability Detail

In the design of the _getAccountLiquidity() function, it loops through all the markets a user has entered. Each additional market increases the computational complexity and thus, the gas cost of this function. When a user attempts to redeem assets, if the user has entered many markets, the gas cost for executing the redemption transaction escalates and can surpass the block gas limit, causing the transaction to fail.

Impact

This vulnerability creates an avenue to lock funds within the protocol. A user who enters an excessive number of markets inflates the gas cost for their redemption transaction beyond the block gas limit, thereby rendering the redemption transaction unexecutable and the assets unredeemable.

Code Snippet

https://github.com/sherlock-audit/2023-05-ironbank/blob/main/ib-v2/src/protocol/pool/IronBank.sol#L1037

    function _getAccountLiquidity(address user) internal view returns (uint256, uint256) {
       ...
        address[] memory userEnteredMarkets = allEnteredMarkets[user]; //E Market array
        for (uint256 i = 0; i < userEnteredMarkets.length; i++) { 
            DataTypes.Market storage m = markets[userEnteredMarkets[i]];
            uint256 supplyBalance = m.userSupplies[user]; 
            uint256 borrowBalance = _getBorrowBalance(m, user); 
            uint256 assetPrice = PriceOracleInterface(priceOracle).getPrice(userEnteredMarkets[i]);
            require(assetPrice > 0, "invalid price");
            uint256 collateralFactor = m.config.collateralFactor;
            if (supplyBalance > 0 && collateralFactor > 0) {
                uint256 exchangeRate = _getExchangeRate(m);
                collateralValue += (supplyBalance * exchangeRate * assetPrice * hh) / 1e36 / FACTOR_SCALE;
            }
           ....
    }

Tool used

Manual Review

Recommendation

Implement a restriction on the number of markets a user can enter. The limit should be designed such that the highest possible gas cost for _getAccountLiquidity() does not exceed the block gas limit.

An alternative solution is to cache the value of a user's account liquidity after each transaction that modifies it. Instead of recalculating the liquidity for each redemption, the cached value is used, reducing the computational complexity and gas cost.

Refactoring the _getAccountLiquidity() function to calculate liquidity over multiple transactions is another possible approach. This would require adjustments to the contract's logic to handle intermediate states of redemption but would allow users who have entered a large number of markets to redeem their assets in parts.