Summary
Reserve prime rate can be higher than the max rate as it can be updated by the Prime Rate Oracle without validation. This may result in a permanent Denial of Service issue in the Lending Pool processes.
Vulnerability Details
Prime Rate Oracle is allowed to update the reserve prime rate by calling the LendingPool::setPrimeRate
function, which in turn calls the ReserveLibrary::setPrimeRate
function. However, when setting the new value, it is not checked against the maximum rate value, which may be exceeded causing a Denial of Service in the functions that change the system global state, since processes will revert in the ReserveLibrary::calculateBorrowRate
function.
ReserveLibrary::calculateBorrowRate
internal function is called by ReserveLibrary::updateInterestRatesAndLiquidity
, which in turn is called after any operation changes the liquidity or debt of the reserve, as documentation states.
> ReserveLibrary.sol
function setPrimeRate( ReserveData storage reserve,ReserveRateData storage rateData,uint256 newPrimeRate) internal {
if (newPrimeRate < 1) revert PrimeRateMustBePositive();
uint256 oldPrimeRate = rateData.primeRate;
if (oldPrimeRate > 0) {
uint256 maxChange = oldPrimeRate.percentMul(500);
uint256 diff = newPrimeRate > oldPrimeRate ? newPrimeRate - oldPrimeRate : oldPrimeRate - newPrimeRate;
if (diff > maxChange) revert PrimeRateChangeExceedsLimit();
}
rateData.primeRate = newPrimeRate;
updateInterestRatesAndLiquidity(reserve, rateData, 0, 0);
emit PrimeRateUpdated(oldPrimeRate, newPrimeRate);
}
> ReserveLibrary.sol
function calculateBorrowRate(
uint256 primeRate,
uint256 baseRate,
uint256 optimalRate,
uint256 maxRate,
uint256 optimalUtilizationRate,
uint256 utilizationRate
) internal pure returns (uint256) {
@> if (primeRate <= baseRate || primeRate >= maxRate || optimalRate <= baseRate || optimalRate >= maxRate) {
revert InvalidInterestRateParameters();
}
... snip
}
Impact
Impact: High
Likelihood: Low
Tools Used
Manual Review
Recommendations
Perform a check of the new prime rate against the max rate in ReserveLibrary::setPrimeRate
.
> ReserveLibrary.sol
function setPrimeRate( ReserveData storage reserve,ReserveRateData storage rateData,uint256 newPrimeRate) internal {
if (newPrimeRate < 1) revert PrimeRateMustBePositive();
uint256 oldPrimeRate = rateData.primeRate;
if (oldPrimeRate > 0) {
uint256 maxChange = oldPrimeRate.percentMul(500); // Max 5% change
uint256 diff = newPrimeRate > oldPrimeRate ? newPrimeRate - oldPrimeRate : oldPrimeRate - newPrimeRate;
if (diff > maxChange) revert PrimeRateChangeExceedsLimit();
}
+ if (newPrimeRate > rateData.maxRate){
+ newPrimeRate = rateData.maxRate;
+ }
rateData.primeRate = newPrimeRate;
updateInterestRatesAndLiquidity(reserve, rateData, 0, 0);
emit PrimeRateUpdated(oldPrimeRate, newPrimeRate);
}