15,000 USDC
View results
Submission Details
Severity: medium
Valid

[M-01] Should check return data from Chainlink aggregators

Summary

The staleCheckLatestRoundData function in the Oraclelib.sol library lacks proper checks on the return values obtained from the Chainlink aggregator. The function fetches asset prices using the latestRoundData function but does not validate the roundId, updatedAt timestamp, and other response parameters. As a result, the contract may be susceptible to receiving stale or incorrect price data from the external Chainlink aggregator, resulting in outdated data being fed to the index price calculations for liquidity.

Vulnerability Details

The vulnerable staleCheckLatestRoundData function fetches the latest asset price from the Chainlink aggregator via the latestRoundData function. However, it fails to perform essential checks on the roundId, updatedAt and other response parameters. The absence of these validation steps leaves the function unable to determine the freshness and accuracy of the price data it receives. As a result, the contract might use outdated or erroneous price information, exposing it to potential issues arising from stale price data.

The vulnerability can manifest in various ways. For instance, if the Chainlink oracle experiences delays or malfunctions, it might return price data that is no longer relevant or has not been updated in time. The contract, lacking validation, will blindly accept this stale data and use it for critical operations, such as index price calculations.

Proof Of Concept

  1. Navigate to the following contract "https://github.com/Cyfrin/2023-07-foundry-defi-stablecoin/blob/main/src/libraries/OracleLib.sol#L21"

  2. Missing validation checks:

function staleCheckLatestRoundData(AggregatorV3Interface priceFeed)
public
view
returns (uint80, int256, uint256, uint256, uint80)
{
(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) =
priceFeed.latestRoundData();
//missing checks for roundIdanswer,startedAt and answeredInRound
uint256 secondsSince = block.timestamp - updatedAt;
if (secondsSince > TIMEOUT) revert OracleLib__StalePrice();
return (roundId, answer, startedAt, updatedAt, answeredInRound);
}

Tools Used

  • VS code

  • Manual review

Recommendations

Consider adding missing checks for the stale data.

For example:

(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) =
priceFeed.latestRoundData();
require(roundId > 0, "Invalid roundId"); // Check for an invalid roundId that is greater than 0
require(answeredInRound >= roundId, "Stale price"); // Check if the response was from a completed round
require(answer > 0, "Invalid answer"); // Check for a valid and non-zero answer
require(startedAt > 0 && startedAt <= block.timestamp, "Invalid startedAt"); // Check for an invalid startedAt timestamp

Support

FAQs

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