Open sherlock-admin3 opened 5 months ago
@arjun-io @panprog @bin2chen66 For the current supported tokens in READ.ME, I think medium severity remains appropriate given they are both stablecoins. Do you agree?
@arjun-io @panprog @bin2chen66 For the current supported tokens in READ.ME, I think medium severity remains appropriate given they are both stablecoins. Do you agree?
I'm not entirely sure how the stablecoin in use matters here? Returning an invalid versions as valid can be very detrimental in markets where invalid versions can be triggered at will (such as in markets that close) which can result in users being able to open or close positions when they shouldn't be able to
The protocol team fixed this issue in the following PRs/commits: https://github.com/equilibria-xyz/perennial-v2/pull/308
The Lead Senior Watson signed off on the fix.
panprog
medium
Requested oracle versions, which have expired, must return this oracle version as invalid, but they return it as a normal version with previous version's price instead
Summary
Each market action requests a new oracle version which must be commited by the keepers. However, if keepers are unable to commit requested version's price (for example, no price is available at the time interval, network or keepers are down), then after a certain timeout this oracle version will be commited as invalid, using the previous valid version's price.
The issue is that when this expired oracle version is used by the market (using
oracle.at
), the version returned will be valid (valid = true
), because oracle returns version as invalid only ifprice = 0
, but thecommit
function sets the previous version's price for these, thus it's not 0.This leads to market using invalid versions as if they're valid, keeping the orders (instead of invalidating them), which is a broken core functionality and a security risk for the protocol.
Vulnerability Detail
When requested oracle version is commited, but is expired (commited after a certain timeout), the price of the previous valid version is set to this expired oracle version:
Later,
Market._processOrderGlobal
reads the oracle version using theoracle.at
, invalidating the order if the version is invalid:However, expired oracle version will return
valid = true
, because this flag is only set tofalse
ifprice = 0
:This means that
_processOrderGlobal
will treat this expired oracle version as valid and won't invalidate the order.Impact
Market uses invalid (expired) oracle versions as if they're valid, keeping the orders (instead of invalidating them), which is a broken core functionality and a security risk for the protocol.
Code Snippet
KeeperOracle._commitRequested
sets_prices
to the last valid version's price for expired versions: https://github.com/sherlock-audit/2024-02-perennial-v2-3/blob/main/perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperOracle.sol#L153-L162Market._processOrderGlobal
reads the oracle version using theoracle.at
, invalidating the order if the version is invalid: https://github.com/sherlock-audit/2024-02-perennial-v2-3/blob/main/perennial-v2/packages/perennial/contracts/Market.sol#L604-L613KeeperOracle.at
returnsvalid = false
only ifprice = 0
, but since expired version has valid price, it will be returned as a valid version: https://github.com/sherlock-audit/2024-02-perennial-v2-3/blob/main/perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperOracle.sol#L109-L112Tool used
Manual Review
Recommendation
Add validity map along with the price map to
KeeperOracle
when recording commited price.