Obedient Lava Monkey
Medium
The lack of a check to ensure the reserve is active in the executeBorrow
function will cause unintended borrowing from disabled reserves, allowing users to exploit reserves that should not permit borrowing.
There is no validation to confirm that the reserve's status is active before processing the borrow. Reserves can be temporarily disabled for maintenance or risk management, and the absence of this check enables borrowing from reserves that should not be accessible.
DataTypes.ReserveData storage reserve = reservesData[params.asset];
// Missing reserve status validation (e.g., reserve.isActive)
reserve.updateState(reserveCache);
In the ReserveLogic
library, reserves have configurations stored in the DataTypes.ReserveConfigurationMap. This includes the isActive
flag, which is intended to determine whether a reserve is available for operations like borrowing, repaying, or supplying liquidity. However, BorrowLogic.executeBorrow
does not check this flag before proceeding with state updates. The getActive
function explicitly checks the ACTIVE_MASK
bit in the reserve's configuration:
function getActive(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) {
return (self.data & ACTIVE_MASK) != 0;
}
This clearly indicates that reserves have an active state flag designed to enable or disable operations like borrowing or supplying liquidity. However, this state is not being validated in the executeBorrow
function of BorrowLogic
.
From ReserveLogic.sol
, reserves maintain their configurations via the ReserveConfiguration
library:
reserveCache.reserveConfiguration = reserve.configuration;
reserveCache.reserveFactor = reserveCache.reserveConfiguration.getReserveFactor();
The ReserveConfiguration
library provides a method (getActive
) to retrieve the active status of a reserve:
function getActive(DataTypes.ReserveConfigurationMap storage self) internal view returns (bool) {
return (self.data & ~ACTIVE_MASK) != 0;
}
When BorrowLogic.executeBorrow
fetches a reserve's data, it does not validate the reserve's active status. For instance:
DataTypes.ReserveData storage reserve = reservesData[params.asset];
// Missing check for reserve.isActive or equivalent
reserve.updateState(reserveCache);
Without this validation, the protocol will process borrowing operations on a reserve that may be disabled due to maintenance, risk mitigation, or other factors, leading to unintended consequences.
- Reserve is marked as inactive (e.g., for maintenance or risk mitigation).
- User attempts to borrow from the inactive reserve.
- The reserve is disabled on-chain but still has sufficient liquidity.
- User identifies a reserve that is disabled but lacks an active status check in
executeBorrow
. - The user borrows from the reserve despite it being marked as inactive.
- The protocol fails to enforce its intended restrictions, allowing unintended borrowing.
The protocol suffers potential financial and operational risks by enabling borrowing from reserves that are supposed to be inactive. This may lead to reserve depletion or destabilization.
Add a validation step in executeBorrow
to ensure the reserve is active before processing the borrowing logic:
require(reserve.isActive, "Reserve is not active for borrowing");