DeFiHardhat
12,000 USDC
View results
Submission Details
Severity: low
Valid

Missing overflow protection in `_capRates` function can lead to broken wells and pumps after a few blocks of unuse

Summary

The _capRates function calculates the max allowed rate as

exp is based on the block difference,

capExponent = ((deltaTimestamp - 1) / capInterval + 1);

So the exp can be assumed to be the difference in block numbers for simplicity. The issue is that the expression given above grows very fast with time, since it is an exponential function. For large exp, there can be an overflow.

The code implements the same formula in the following way, in multiple places in the code.

bytes16 tempExp = ABDKMathQuad.ONE.add(crp.maxRateChanges[i][j]).powu(capExponent);
crv.rLimit = tempExp.cmp(MAX_CONVERT_TO_128x128) != -1
? crv.rLimit = type(uint256).max

The first line above shows the calculation of a temporary exponentiated term. This will later be multiplied with the last stored ratio. The code also operates in 128X128 fixed point arithmetic, so the above is converted to 128X128 as well. But 128X128 numbers can hold far smaller quantities, so an overflow check is needed before this conversion.

The second line in the snippet above does that. If the intermediate result is above a certain value, it is not converted into 128X128, and the calculation is skipped. This prevents the overflow, and is thus overflow protection.

However, this is missing in the case when rates decrease in the _capRates function. For decreasing rates, the same exponentiated term is calculated, but this is ultimately then divided from the last rate.

crv.rLimit = crv.rLast.mulDiv(
ABDKMathQuad.ONE.div(ABDKMathQuad.ONE.add(crp.maxRateChanges[j][i])).powu(capExponent).to128x128()
.toUint256(),
CAP_PRECISION2
);

As seen above, the exponentiated term is converted to 128X128, but there is no check if this is possible! This might lead to an overflow, and the well might break.

If a long time has passed since the last update, the capExponent term will start to grow. So the propensity of this problem starts to grow as well with time.

For some rough numbers, we assume gamma = 1, so the code allows a 100% change per block effectively. Then I ran the test below to find the limits of this conversion.

function test(uint256 y) public view returns(uint256) {
// bytes16 TWO = add(ONE,ONE);
bytes16 TWO = from18(1.5e18);
return(uint256(to128x128(powu(TWO,y))));
}

We basically exponentiate the variable TWO and convert it to 128x128. For one case, TWO is 2, and for the other case its 1.5.

From running these tests, I saw that exponentiating 2 overflows when converting to 128X128 when the power is 126. Exponentiating 1.5 overflows when the power is 217.

In other words, if the ratio decreases due to a swap, and a user has not touched that well for 127 blocks, if the gamma rate was 100% change per block, the well will stop working since the exponentiated value will overflow.

Similarly for a gamma of 50% per block, the well overflows after leaving untouched for 218 blocks.

Whatever the gamma may be, the well will break after a certain number of blocks of unuse.

In all other cases in the code, there is either overflow protection like shown above, or the exponentiation is on a decaying function (1 - x)^n, which will never overflow. This is the only case where the exponentiation is on a growing function without protection.

Impact

If a swap reduces the rate of the well, and then the well is left untouched for a long time, the well will become unusable. This is because when it will be finally updated, it will have a tendency to overflow since the exponentiation is high and there is no overflow protection.

Tools Used

Foundry

Recommendations

Add the same overflow protection. If the number cannot be converted to 128X128, ratio limit should be 0.

Updates

Lead Judging Commences

giovannidisiena Lead Judge
about 1 year ago
giovannidisiena Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Cap rates validation

carrotsmuggler Submitter
about 1 year ago
giovannidisiena Lead Judge
about 1 year ago
giovannidisiena Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Cap rates validation

Support

FAQs

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