sherlock-audit / 2024-09-predict-fun-judging

5 stars 4 forks source link

Atharv - Overflow in Debt Calculation Prevents Loan Repayment, Leading to Loss of Collateral #228

Open sherlock-admin2 opened 1 month ago

sherlock-admin2 commented 1 month ago

Atharv

High

Overflow in Debt Calculation Prevents Loan Repayment, Leading to Loss of Collateral

Summary

In the protocol's loan repayment functionality, an overflow issue in the _calculateDebt function prevents the borrower from repaying their loan, which may result in the loss of collateral. The issue arises due to the compound interest calculation formula, which quickly overflows uint256 limits at high interest rates and short time frames. Specifically, with the maximum allowable interest rate per second, the debt calculation overflows in just 7 seconds, causing the pow function to revert, leaving borrowers unable to repay their debt.

Root Cause

When a borrower takes a loan, the protocol allows them to repay it using a repay function, which calculates the current debt using the _calculateDebt function. The debt calculation formula is as follows: Code

debt = (loanAmount * interestRatePerSecond.pow(timeElapsed)) / InterestLib.ONE;

The pow function, which calculates interestRatePerSecond.pow(timeElapsed), uses an iterative squaring approach. However, with the maximum interest rate per second set at 1e18 + 146,247,483,013, the compound interest formula grows extremely fast. Given the maximum allowable interest rate, the debt calculation overflows the uint256 limit in just 7 seconds, causing the pow function to revert.

uint256 public constant ONE = 10 ** 18;
uint256 public constant TEN_THOUSAND_APY = 146_247_483_013;

function pow(uint256 _base, uint256 _exponent) public pure returns (uint256) {
    if (_exponent == 0) {
        return ONE;
    } else if (_exponent % 2 == 0) {
        uint256 half = pow(_base, _exponent / 2);
        return half * half / ONE;
    } else {
        return _base * pow(_base, _exponent - 1) / ONE;
    }
}

With interestRatePerSecond set to the maximum of (1e18 + 146_247_483_013), the calculation reaches the overflow limit quickly, making the debt calculation revert.

Internal pre-conditions

  1. Loan interest rate per second value should be pretty high.

External pre-conditions

No response

Attack Path

Suppose a borrower takes a loan of 1e18 (1 token) at the maximum interest rate of (1e18 + 146_247_483_013). After only 7 seconds, the pow function calculation will overflow and revert, preventing the borrower from successfully repaying their loan and retrieving their collateral.

  1. The borrower takes out a loan of 1e18.
  2. The interest rate per second is set to 1e18 + 146_247_483_013.
  3. If the borrower tries to repay after 7 seconds, _calculateDebt will attempt to calculate debt using the pow function.
  4. The pow function will overflow the uint256 limit, causing a revert, preventing the borrower from repaying their loan.
  5. As a result, the borrower will lose access to their collateral.

This overflow makes it impossible for the borrower to repay their debt once the overflow threshold is reached, potentially leading to loss of funds.

Impact

Borrowers are unable to repay their loans due to an overflow error in the debt calculation, leading to the loss of their collateral. This issue significantly impacts the protocol's usability, as any borrower with a high interest rate could be affected by this bug

PoC

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "forge-std/Test.sol";
import "./InterestLib.sol"; // Replace with the path to InterestLib contract/library

contract OverflowPowTest is Test {
    uint256 constant MAX_INTEREST_RATE_PER_SECOND = 1e18 + 146_247_483_013;

    function testPowOverflow() public {
        // Set up a base and exponent that would cause overflow in the pow function
        uint256 base = MAX_INTEREST_RATE_PER_SECOND;
        uint256 exponent = 7; // This is the threshold to cause overflow in this scenario

        // Expect the pow function to revert due to overflow
        vm.expectRevert(); // Expect this to revert
        InterestLib.pow(base, exponent);
    }
}

Mitigation

No response