Open code423n4 opened 1 year ago
thereksfour marked the issue as primary issue
This issue was known and was discussed internally. Unfortunately this occurred in the private copy of the repo that the devs use to coordinate while C4 contests are ongoing. I've attached a screenshot of the discussion, though it is up to C4 how to treat this ultimately. We could probably provide repo access to a member of the C4 team if asked.
We decided not to pursue this direction as it introduced a large number of changes, and it seems acceptable to have the worst-case behavior of using 100% of the last saved price for up to one oracleTimeout too long.
tbrent marked the issue as sponsor disputed
Agree that sponsors not address it. And this issue will be considered as medium risk under the C4 criteria.
2 — Med: Assets not at direct risk, but the function of the protocol or its availability could be impacted, or leak value with a hypothetical attack path with stated assumptions, but external requirements.
thereksfour marked the issue as satisfactory
thereksfour marked the issue as selected for report
Lines of code
https://github.com/reserve-protocol/protocol/blob/9ee60f142f9f5c1fe8bc50eef915cf33124a534f/contracts/plugins/assets/Asset.sol#L139-L145
Vulnerability details
When the
tryPrice()
function revert, for example oracle timeout, theAsset.lotPrice
will use a decayed historical value:And the delta time is from the last price saved time. If the delta time is greater than oracle timeout, historical price starts decaying.
But the last price might be saved at the last second of the last oracle timeout period. So the
Asset.lotPrice
will double the oracle timeout in the worst case.Impact
The
Asset.lotPrice
will double the oracle timeout in the worst case. When the rewards need to be sold or basket is rebalancing, if the price oracle is offline temporarily, theAsset.lotPrice
will use the last saved price in max two oracle timeout before the historical value starts to decay. It increases the sale/buy price of the asset.Proof of Concept
The
lastSave
is updated in therefresh()
function, and it's set to the currentblock.timestamp
instead of theupdateTime
from the chainlink feed:But in the
OracleLib
, the oracle time is checked for the delta time ofblock.timestamp - updateTime
:So if the last oracle feed updateTime is
block.timestamp - priceTimeout
, the timeout check will be passed and lastSave will be updated to block.timestamp. And the lotPrice will start to decay fromlastSave + priceTimeout
. However when it starts, it's been 2 * priceTimeout since the last oracle price update.Tools Used
Manual review
Recommended Mitigation Steps
Starts lotPrice decay immediately or updated the
lastSave
toupdateTime
instead ofblock.timestamp
.Assessed type
Context