(, int256 ohmEthPriceInt, , uint256 updatedAt, ) = _ohmEthPriceFeed.latestRoundData();
// Use a multiple of observation frequency to determine what is too old to use.
// Price feeds will not provide an updated answer if the data doesn't change much.
// This would be similar to if the feed just stopped updating; therefore, we need a cutoff.
if (updatedAt < block.timestamp - 3 * uint256(observationFrequency))
revert Price_BadFeed(address(_ohmEthPriceFeed));
ohmEthPrice = uint256(ohmEthPriceInt);
int256 reserveEthPriceInt;
(, reserveEthPriceInt, , updatedAt, ) = _reserveEthPriceFeed.latestRoundData();
if (updatedAt < block.timestamp - uint256(observationFrequency))
revert Price_BadFeed(address(_reserveEthPriceFeed));
reserveEthPrice = uint256(reserveEthPriceInt);
For both _ohmEthPriceFeed and _reserveEthPriceFeed the validation of the result when calling latestRoundData is insufficient. The code has a correct value check for the updatedAt value, but it is missing one to check if price is actually a positive number and also to check the answeredInRound return value to see if the price is stale.
Impact
The PRICE.sol contract and logic are a core part of the protocol and using a zero or a stale price can result in the protocol returning an incorrect moving average or a zero one. This means the prices in the Range module will be set incorrectly.
Recommendation
Change the latestRoundData logic to the following (you can use custom errors instead require statements as well):
Lines of code
https://github.com/code-423n4/2022-08-olympus/blob/549b96bcf8b97807738572605f6b1e26b33ef411/src/modules/PRICE.sol#L161 https://github.com/code-423n4/2022-08-olympus/blob/549b96bcf8b97807738572605f6b1e26b33ef411/src/modules/PRICE.sol#L170
Vulnerability details
Proof of Concept
For both
_ohmEthPriceFeed
and_reserveEthPriceFeed
the validation of the result when callinglatestRoundData
is insufficient. The code has a correct value check for theupdatedAt
value, but it is missing one to check if price is actually a positive number and also to check theansweredInRound
return value to see if the price is stale.Impact
The
PRICE.sol
contract and logic are a core part of the protocol and using a zero or a stale price can result in the protocol returning an incorrect moving average or a zero one. This means the prices in theRange
module will be set incorrectly.Recommendation
Change the
latestRoundData
logic to the following (you can use custom errors instead require statements as well):Change the code exactly the same way for both
latestRoundData
calls, and also leave the current check forupdatedAt
.