Steadefi

Steadefi
DeFiHardhatFoundryOracle
35,000 USDC
View results
Submission Details
Severity: low
Valid

A negative chainlink price would be considered valid

Summary

The response.answer resulting from Chainlink calls is of type int256. If there is an issue with Chainlink validators and the call yields a gradually decreasing value or becomes suddenly negative, an invalid price will be accepted. This could happen either because the previous or current price is incorrect (negative) and the deviation is insufficient to trigger a revert, or because both prices are invalid (negative).

Vulnerability Details

function _badChainlinkResponse(ChainlinkResponse memory response) internal view returns (bool) {
// Check for response call reverted
if (!response.success) { return true; }
// Check for an invalid roundId that is 0
if (response.roundId == 0) { return true; }
// Check for an invalid timeStamp that is 0, or in the future
if (response.timestamp == 0 || response.timestamp > block.timestamp) { return true; }
// Check for non-positive price
if (response.answer == 0) { return true; }
return false;
}

Impact

If Chainlink provides inaccurate prices, the protocol lacks all the necessary protections to prevent these erroneous prices from being accepted.

I have written this quick POC demonstrating one way/scenario in which this can occur.

// POC of badResonse of chainlink oracle
function test_bad_response() external {
vm.startPrank(user1);
// set price feeds, bad to have
previousRoundData.roundId = 1;
previousRoundData.answer = -1600e8; // negative price
previousRoundData.startedAt = 1632931200;
previousRoundData.updatedAt = 1632931200;
previousRoundData.answeredInRound = 1;
mockWETHPriceFeed.setPreviousRoundData(previousRoundData);
currentRoundData.roundId = 2;
currentRoundData.answer = -1600e8; // negative price
currentRoundData.startedAt = 1632934800;
currentRoundData.updatedAt = 1632934800;
currentRoundData.answeredInRound = 2;
mockWETHPriceFeed.setCurrentRoundData(currentRoundData);
(int256 answer, uint8 decimals) = chainlinkOracle.consult(_WETH);
console2.log("answer: %d", answer);
console2.log("decimals: %d", decimals);
Output:
// Running 5 tests for test/chainlink/ChainlinkARBOracleTest.t.sol:ChainlinkARBOracleTest
// [PASS] test_bad_response() (gas: 111615)
// Logs:
// answer: -160000000000
// decimals: 8
}

Tools Used

Manual Review

Recommendations

Check for: answer <= 0 not answer == 0 like following:

Change this line:

// Check for non-positive price
if (response.answer == 0) { return true; }

By this:

// Check for non-positive price
if (response.answer <= 0) { return true; }
Updates

Lead Judging Commences

hans Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Chainlink oracle answer can be negative

Very low likelihood -> evaluate the severity to LOW

Support

FAQs

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