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

Lacking proper validations leads to consuming stale or incorrect price data

Summary

The staleCheckLatestRoundData() can return stale or incorrect price data due to lacking proper validations of Chainlink's returned parameters.

Vulnerability Details

The staleCheckLatestRoundData() gets the collateral token's price data by executing the priceFeed.latestRoundData(). Even though the function would check the received updatedAt parameter against the TIMEOUT constant, that is still not enough to detect stale or incorrect price data.

Consider the following conditions.

  1. If the answeredInRound < roundId, that indicates the returned answer is an old/stale value.

  2. If the answer is less than or equal to 0, that indicates an incorrect price return.

The staleCheckLatestRoundData() would not be able to detect the above conditions. As a result, the function will return stale or incorrect price data.

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

https://github.com/Cyfrin/2023-07-foundry-defi-stablecoin/blob/d1c5501aa79320ca0aeaa73f47f0dbc88c7b77e2/src/libraries/OracleLib.sol#L26-L27

Impact

The stale or incorrect price data can cause the DSC protocol's functions (e.g., mintDsc(), burnDsc(), redeemCollateral(), and liquidate()) to operate incorrectly, affecting the protocol's disruption.

Tools Used

Manual Review

Recommendations

I recommend adding the require checks, as shown below, to detect stale or incorrect price data.

function staleCheckLatestRoundData(AggregatorV3Interface priceFeed)
public
view
returns (uint80, int256, uint256, uint256, uint80)
{
(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) =
priceFeed.latestRoundData();
+ require(answeredInRound >= roundId, "Stale price");
+ require(updatedAt != 0,"Round not complete");
+ require(answer > 0,"Chainlink's answer <= 0");
uint256 secondsSince = block.timestamp - updatedAt;
if (secondsSince > TIMEOUT) revert OracleLib__StalePrice();
return (roundId, answer, startedAt, updatedAt, answeredInRound);
}

Support

FAQs

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