Tadle

Tadle
DeFiFoundry
27,750 USDC
View results
Submission Details
Severity: medium
Invalid

Overflow in `getRefundAmount` Function

Summary

The getRefundAmount function contains a overflow vulnerability when performing arithmetic operations using the Math.mulDiv function. Specifically, the calculation in Math.mulDiv can result in an overflow when large input values are used. The getRefundAmount function is widely used in the codebase, and if exploited, it could lead to incorrect refund calculations.

Vulnerability Details

Vulnerable Code

function getRefundAmount(
OfferType _offerType,
uint256 _amount,
uint256 _points,
uint256 _usedPoints,
uint256 _collateralRate
) internal pure returns (uint256) {
uint256 usedAmount = Math.mulDiv(
_amount,
_usedPoints,
_points,
Math.Rounding.Ceil
);
if (_offerType == OfferType.Bid) {
return _amount - usedAmount;
}
return
Math.mulDiv(
_amount - usedAmount,
_collateralRate,
Constants.COLLATERAL_RATE_DECIMAL_SCALER,
Math.Rounding.Floor
);
}

https://github.com/Cyfrin/2024-08-tadle/blob/main/src/libraries/OfferLibraries.sol#L63C1-L88C6

Description

The getRefundAmount function performs critical arithmetic operations to calculate the refund amount based on the type of offer. It uses the Math.mulDiv function, which calculates floor(x * y / denominator) with full precision. However, the mulDiv function can overflow under certain conditions, particularly when handling very large values for _amount, _usedPoints, _points, or _collateralRate.

Given that the function is used throughout the codebase, this overflow could lead to incorrect refund calculations. The vulnerability is particularly dangerous as it can remain unnoticed in normal operations but could be triggered by specific large input values.

Proof of Concept (PoC)

The following Fuzz test case demonstrates the overflow condition in the getRefundAmount function:

function testFuzz_getRefundAmount(
uint8 offerType,
uint256 amount,
uint256 points,
uint256 usedPoints,
uint256 collateralRate
) public {
offerType = offerType % 2; // ensure offerType is either 0 (Bid) or 1 (Ask)
points = points == 0 ? 1 : points; // avoid zero points
usedPoints = usedPoints > points ? points : usedPoints; // ensure usedPoints <= points
uint256 usedAmount = Math.mulDiv(amount, usedPoints, points, Math.Rounding.Ceil);
uint256 expectedRefundAmount;
if (RefundCalculator.OfferType(offerType) == RefundCalculator.OfferType.Bid) {
expectedRefundAmount = amount - usedAmount;
} else {
expectedRefundAmount = Math.mulDiv(amount - usedAmount, collateralRate, 1000000, Math.Rounding.Floor);
}
uint256 refundAmount = refundCalculator.getRefundAmount(
RefundCalculator.OfferType(offerType),
amount,
points,
usedPoints,
collateralRate
);
assertEq(refundAmount, expectedRefundAmount);
}

Test Output

Running the above test case with large values results in the following failure:

[FAIL. Reason: MathOverflowedMulDiv(); counterexample: calldata=0x0232e01c000000000000000000000000000000000000000000000000000000000000000900000000000000000000000000000000000000021bd43bc887f5de21b5da9d1800036a82dd84c89cdd62338ea5654cda2bf2e96020a78428ef4bde059eaeb288000000000000000000000000000000000000000000000c12296a6995b6a9d9970000000000000000001632dad7d0594f6815ef3da1c141b9d395c4ade67fc84f args=[9, 167068994886231578279453302040 [1.67e29], 6035654428802163741161918876335697608570609959432798183353405705861116552 [6.035e72], 57003423501557336496535 [5.7e22], 2126208429084721616960285645673889888440555536501426255 [2.126e54]]] testFuzz_getRefundAmount(uint8,uint256,uint256,uint256,uint256) (runs: 0, μ: 0, ~: 0)

Impact

The overflow vulnerability can lead to incorrect refund amounts being calculated. As this function is utilized in various parts of the codebase.

Tools Used

  • Manual code review

  • Foundry for fuzz testing

Recommendations

Implement input validation to ensure that the values passed to getRefundAmount are within safe bounds. This can help mitigate the risk of overflow by avoiding extreme input values.

Updates

Lead Judging Commences

0xnevi Lead Judge
about 1 year ago
0xnevi Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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