Closed sherlock-admin closed 1 year ago
While the general issue was already reported in the Veridise audit (006), where the protocol responded that it can't underflow, this submission correctly identifies that it can underflow in the "second use case" mentioned above; if product is paused or the external oracle is not working properly (sequencer is down on L2)
.
The second use case is similarly not possible because of what is described in the Veridise audit. The end version (toOracleVersion
) is determined by the following snippet:
versionComplete = Math.max(versionStarted, product.latestVersion());
In this case, product.latestVersion()
will be OraclefVersion3
not OraclefVersionCurrent
. This is because sync()
is called before the product.latestVersion
is transitioned.
nobody2018
high
Program.complete may cause subtraction overflow in some cases
Summary
Program.complete
is called when an incentive program is completed. There is [a line of code](https://github.com/sherlock-audit/2023-05-perennial/blob/main/perennial-mono/packages/perennial/contracts/incentivizer/types/Program.sol#L69) inside the function to calculate theinactiveDuration
, where it is possible to overflow/underflow in some cases.Vulnerability Detail
Let's see the code of
Program.complete
:If
programInfo.duration
is less than(toOracleVersion.timestamp - fromOracleVersion.timestamp)
, then tx will revert. The flow of the entire tx is as follows:Here are two use cases to prove that
programInfo.duration
will be less than(toOracleVersion.timestamp - fromOracleVersion.timestamp)
.The first use case:
programInfo.duration
is assigned when the program is created, and the duration cannot be guaranteed to be a multiple of(toOracleVersion.timestamp - fromOracleVersion.timestamp)
. For simplicity, assume that the timestamp of the adjacent OraclefVersion is 60s, and theprogramInfo.duration
is 150s.In this case
programInfo.duration
is less than(toOracleVersion.timestamp - fromOracleVersion.timestamp)
. Because150 < (180 - 0)
.The second use case: The product is paused or the external oracle is not working properly (sequencer is down on L2). This will cause all OracleVersions to be skipped during this period.
For simplicity, assume that the timestamp of the adjacent OraclefVersion is 60s, and the programInfo.duration is 600s.
In this case
programInfo.duration
is less than(toOracleVersion.timestamp - fromOracleVersion.timestamp)
. Because600 < (780 - 0)
.Impact
Although both use cases are uncommon, if one of them occurs, the product would be useless. No one can open/close positions/liquidate, nor withdraw collateral.
The impact is huge
. BecauseProduct._settle()
is the core function. Most interfaces that users can call depend on it, such asopenTakeFor
/closeTakeFor
/openMakeFor
/closeMakeFor
/closeAll
in Product.sol,withdrawFrom
/liquidate
in Collateral.sol.Code Snippet
https://github.com/sherlock-audit/2023-05-perennial/blob/main/perennial-mono/packages/perennial/contracts/incentivizer/types/Program.sol#L69
https://github.com/sherlock-audit/2023-05-perennial/blob/main/perennial-mono/packages/perennial/contracts/product/Product.sol#L96
https://github.com/sherlock-audit/2023-05-perennial/blob/main/perennial-mono/packages/perennial/contracts/product/Product.sol#L589
https://github.com/sherlock-audit/2023-05-perennial/blob/main/perennial-mono/packages/perennial/contracts/product/Product.sol#L69
Tool used
Manual Review
Recommendation