The vulnerability is related to missing safety checks for validating data returned from Chainlink's latestRoundData
function, which fetches price feeds. The missing checks include ensuring the validity and completeness of the requested round, verifying that the price is greater than zero, and checking the freshness of the price data.
The impact of these missing safety checks can lead to various issues, such as using data from an invalid round for computations, encountering zero prices, using stale price data for calculations, and basing computations on incorrect prices.
To mitigate the vulnerability, I recommend defining specific errors for different scenarios and two variables representing the minimum and maximum price ranges. Additionally, the recommended mitigation steps include adding the necessary safety checks to the staleCheckLatestRoundData()
function to ensure the validity and reliability of the price data.
Certain safety checks that should be used to validate data returned from latestRoundData
are missing:
require(updatedAt > 0)
: This checks whether the requested round is valid and complete. A description of the parameter validation can be found in Chainlink's documentaion
require(answer > 0)
: While the price is expected to be greater than zero, the code may consume zero prices. This check should be added before returning the price.
require(answerInRound >= roundId)
: The answeredInRound
must be greater than or equal to roundId
. If answeredInRound
is less than roundId
, the answer is carried over. If answeredInRound
is equal to roundId
, then the answer is fresh.
require(answer > minThreshold && answer < maxThreshold)
: It is important to be aware of minAnswer
and maxAnswer
of the Chainlink oracle. These values are not allowed to be reached or surpassed. You have to consider having off-chain monitoring to identify when the market price moves out of the [minAnswer, maxAnswer]
range. Also, you can consider minAnswer
and maxAnswer
, and if the result of the oracle is equal to minAnswer
or maxAnswer
, you can revert the execution.
Consider the following cases:
When updatedAt == 0
means the requested round is not valid an complete. Then, the computations will be based on data from an invalid round.
When answer == 0
very detrimental cases can happen. For example, this can cause the liquidation of all of the users that use the collateral whose price is deemed to be zero.
When answerInRound < roundId
means that the given price is not fresh and the computations will be using stale price.
The returned price, answer
, would not be out of the [minAnswer, maxAnswer]
range. When the actual price is less than minAnswer
or more than maxAnswer
, minAnswer
or maxAnswer
will be returned. In these cases, the following computations would be based on the wrong price.
Manual Review
In the first step, define the following errors:
Also, define two variables that show the minimum and maximum range of price that the price can deviate from:
Finally, add the following checks to the staleCheckLatestRoundData()
function:
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.