DeFiFoundry
60,000 USDC
View results
Submission Details
Severity: high
Invalid

Underflow in PerpMarket::getProportionalElapsedSinceLastFunding

Summary

An underflow vulnerability exists in the getProportionalElapsedSinceLastFunding function of the PerpMarket library. This can occur if block.timestamp is less than self.lastFundingTime, potentially due to manipulation or unexpected behavior in the blockchain environment.

Vulnerability Details

The function getProportionalElapsedSinceLastFunding performs a subtraction operation between block.timestamp and self.lastFundingTime without verifying that block.timestamp is greater than or equal to self.lastFundingTime. This can lead to an underflow, causing the function to revert.

function getProportionalElapsedSinceLastFunding(Data storage self) internal view returns (UD60x18) {
return ud60x18Convert(block.timestamp - self.lastFundingTime).div(
ud60x18Convert(Constants.PROPORTIONAL_FUNDING_PERIOD)
);
}

POC

  • Copy this fuzz test to a folder inside the test folder:

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;
import "forge-std/Test.sol";
import "../../src/perpetuals/leaves/PerpMarket.sol";
contract FuzzPerpMarket is Test {
using PerpMarket for PerpMarket.Data;
PerpMarket.Data marketData;
function setUp() public {
marketData.lastFundingTime = block.timestamp + 1;
}
function testUnderflow(uint256 timestamp) public {
// Ensure the timestamp is valid
if (timestamp < marketData.lastFundingTime) {
timestamp = marketData.lastFundingTime;
}
vm.warp(timestamp);
marketData.getProportionalElapsedSinceLastFunding();
}
}
  • Run forge test --match-contract FuzzPerpMarket -vvv

  • Output: Ran 1 test for test/audit-test/FuzzPerpMarket.sol:FuzzPerpMarket [FAIL. Reason: PRBMath_UD60x18_Convert_Overflow(37818181912672474119587692210219731251934813751338583140616424542220949830011 [3.781e76]); counterexample: calldata=0x63037dcc539c5519953fd601f8441381fe8551b4b0729c4fb1ecec03267a11cf3324dbfc args=[37818181912672474119587692210219731251934813751338583140616424542222630050812 [3.781e76]]] testUnderflow(uint256) (runs: 4, μ: 6056, ~: 6083) Traces: [5742] FuzzPerpMarket::testUnderflow(37818181912672474119587692210219731251934813751338583140616424542222630050812 [3.781e76]) ├─ [0] VM::warp(37818181912672474119587692210219731251934813751338583140616424542222630050812 [3.781e76]) │ └─ ← [Return] └─ ← [Revert] PRBMath_UD60x18_Convert_Overflow(37818181912672474119587692210219731251934813751338583140616424542220949830011 [3.781e76])

The counterexample provided by the test shows the specific input that caused the overflow:

  • Input Value: 37818181912672474119587692210219731251934813751338583140616424542222630050812 (approximately 3.781e76)

  • Calldata: 0x63037dcc539c5519953fd601f8441381fe8551b4b0729c4fb1ecec03267a11cf3324dbfc

The execution trace shows the steps leading to the overflow:

  • Function Call: FuzzPerpMarket::testUnderflow(37818181912672474119587692210219731251934813751338583140616424542222630050812)

  • VM Warp: The virtual machine attempts to warp to the given large value.

  • Revert: The operation reverts due to the overflow in PRBMath_UD60x18_Convert_Overflow.

The test failure indicates that contract does not handle extremely large values correctly, leading to an overflow. This is a critical bug that needs to be addressed to ensure the contract's robustness and correctness.

Impact

Severity: High

  • Impact on the protocol: The underflow can cause the contract to revert, potentially halting important contract operations and disrupting market functionality. This can lead to severe disruption of protocol functionality or availability.

  • Likelihood of exploitation: High, as it can be triggered through manipulation of the block.timestamp or unexpected behavior in the blockchain environment.

Tools Used

Manual review and foundry

Recommendations

Add a check to ensure block.timestamp is not less than self.lastFundingTime before performing the subtraction in getProportionalElapsedSinceLastFunding.

function getProportionalElapsedSinceLastFunding(Data storage self) internal view returns (UD60x18) {
+ require(block.timestamp >= self.lastFundingTime, "Underflow detected");
return ud60x18Convert(block.timestamp - self.lastFundingTime).div(
ud60x18Convert(Constants.PROPORTIONAL_FUNDING_PERIOD)
);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge
10 months ago
inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Lack of quality

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.