Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chainlink's latestRoundData might return stale or incorrect results #69

Open
howlbot-integration bot opened this issue Jun 17, 2024 · 3 comments
Open
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working edited-by-warden M-04 primary issue Highest quality submission among a set of duplicates 🤖_91_group AI based duplicate group recommendation satisfactory satisfies C4 submission criteria; eligible for awards selected for report This submission will be included/highlighted in the audit report sponsor confirmed Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity") sufficient quality report This report is of sufficient quality

Comments

@howlbot-integration
Copy link

Lines of code

https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/PriceFeed.sol#L46

Vulnerability details

Impact

In the PriceFeed contract, the protocol uses a ChainLink aggregator to fetch the latestRoundData(), but there is no check if the return value indicates stale data. The only check present is for the quoteAnswer to be > 0, however, this alone is not sufficient.

function getSqrtPrice() external view returns (uint256 sqrtPrice) {
@>        (, int256 quoteAnswer,,,) = AggregatorV3Interface(_quotePriceFeed).latestRoundData(); // missing additional checks

        IPyth.Price memory basePrice = IPyth(_pyth).getPriceNoOlderThan(_priceId, VALID_TIME_PERIOD);

        require(basePrice.expo == -8, "INVALID_EXP");

        require(quoteAnswer > 0 && basePrice.price > 0);

        uint256 price = uint256(int256(basePrice.price)) * Constants.Q96 / uint256(quoteAnswer);
        price = price * Constants.Q96 / _decimalsDiff;

        sqrtPrice = FixedPointMathLib.sqrt(price);
    }

The protocol mentions that:

Attacks that stem from the TWAP being extremely stale compared to the market price within its period (currently 30 minutes) are a known risk. As a general rule, only price manipulation issues that can be triggered by manipulating the price atomically from a normal pool or oracle state are valid.

However, this stale period check is only currently applied to the Pyth integration, where the ChainLink feed is not considered for stale data.

This could lead to stale prices according to the Chainlink documentation:

https://docs.chain.link/docs/historical-price-data/#historical-rounds
https://docs.chain.link/docs/faq/#how-can-i-check-if-the-answer-to-a-round-is-being-carried-over-from-a-previous-round

This discrepancy could have the protocol produce incorrect values for very important functions in different places across the system, such as GammaTradeMarket, PositionCalculator, LiquidationLogic, etc.

Proof of Concept

https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/PriceFeed.sol#L46
https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/PositionCalculator.sol#L141

Tools Used

Manual review

Recommended Mitigation Steps

Consider adding missing checks for stale data.

@@ -43,7 +43,10 @@ contract PriceFeed {
 
     /// @notice This function returns the square root of the baseToken price quoted in quoteToken.
     function getSqrtPrice() external view returns (uint256 sqrtPrice) {
-        (, int256 quoteAnswer,,,) = AggregatorV3Interface(_quotePriceFeed).latestRoundData();
+        (uint80 quoteRoundID, int256 quoteAnswer,, uint256 quoteTimestamp, uint80 quoteAnsweredInRound) =
+            AggregatorV3Interface(_quotePriceFeed).latestRoundData();
+        require(quoteAnsweredInRound >= quoteRoundID, "Stale price!");
+        require(quoteTimestamp != 0, "Round not complete!");
+        require(block.timestamp - quoteTimestamp <= VALID_TIME_PERIOD);

Assessed type

Oracle

@howlbot-integration howlbot-integration bot added 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value 🤖_91_group AI based duplicate group recommendation bug Something isn't working edited-by-warden primary issue Highest quality submission among a set of duplicates sufficient quality report This report is of sufficient quality labels Jun 17, 2024
howlbot-integration bot added a commit that referenced this issue Jun 17, 2024
@syuhei176 syuhei176 added the sponsor confirmed Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity") label Jun 17, 2024
@alex-ppg
Copy link

The Warden has demonstrated how the Chainlink oracle employed by the system does not impose any staleness check, permitting misbehavior in the Chainlink system to not be detected by the system and the system to continue utilizing a stale price, similar to the Luna flash crash.

I believe a medium-risk rating is appropriate given the low likelihood of such an event but the devastating consequences it could result in.

A subset of the duplicates have been penalized for not properly justifying why the staleness check should be applied, for advising an incorrect alleviation (i.e. round ID based), and / or for being of lower quality than acceptable.

@c4-judge
Copy link
Contributor

alex-ppg marked the issue as satisfactory

@c4-judge c4-judge added satisfactory satisfies C4 submission criteria; eligible for awards selected for report This submission will be included/highlighted in the audit report labels Jun 28, 2024
@c4-judge
Copy link
Contributor

alex-ppg marked the issue as selected for report

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working edited-by-warden M-04 primary issue Highest quality submission among a set of duplicates 🤖_91_group AI based duplicate group recommendation satisfactory satisfies C4 submission criteria; eligible for awards selected for report This submission will be included/highlighted in the audit report sponsor confirmed Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity") sufficient quality report This report is of sufficient quality
Projects
None yet
Development

No branches or pull requests

4 participants