# Invalid oracle versions can cause desync of global and local positions making protocol lose funds and being unable to pay back all users

Invalid oracle versions cause global and local positions to settle via different paths, desyncing PnL accounting so total user collateral exceeds deposited funds.

- Payout: Contest reward
- Severity: high
- Program: Perennial
- Credited: panprog
- Disclosed: 2023-07-17
- Write-up quality: 8/10
- Original: https://github.com/sherlock-audit/2023-07-perennial-judging/issues/49
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-07-perennial%3AH-2
- Tags: domain:web3 impact:funds-loss impact:integrity kind:contest platform:sherlock sev:high tech:solidity technique:chained technique:source-review vuln:accounting-error vuln:oracle-manipulation

**Target:** Perennial V2 Market.sol settlement logic (_settle/_processPositionGlobal/_processPositionLocal)
**Root cause:** When an oracle version is invalid, pending positions are invalidated to the previous latest position, but global and local positions can settle against different next-valid oracle versions, so they accumulate PnL over different position states.
**Steps:**
1. User B opens maker=2, collateral=100 at T=99 (position ts=100).
2. User A opens long=1, collateral=100 (pending ts=100).
3. Oracle version at ts=100 is invalid (skipped).
4. At T=201 user A closes long (pending long=1 ts=100; long=0 ts=300).
5. Oracle commits ts=200 price=$100; user B settles, advancing global latest to long=1 at ts=200.
6. Oracle commits ts=300 price=$90; both settle.
7. Global accumulates [maker=2,long=1] from $100->$90: longPnl=-$10, makerPnl=+$10.
8. User B local accumulates only [maker=2], gaining +$10 -> collateral $110.
9. User A's pending long=1 at ts=100 is invalidated to long=0, fees zeroed, collateral stays $100.
10. Total withdrawable $210 vs $200 deposited; last withdrawer short $10.
**How found:** Code reading of _settle: noticed the loop invalidates positions with invalid oracle timestamps to the previous latest, but the later sync step advances latest to a valid version — so early-timestamp invalid positions become valid while late ones are skipped, producing divergent global vs local settlement paths.
**Exploitation:** No attacker capital needed; any user trading around a skipped oracle version triggers the desync. A malicious user can deliberately open a position whose timestamp hits an invalid oracle version, then modify it, letting another user settle after the next valid version, inflating one side's collateral at the protocol's expense (bank run / last user unable to withdraw).
**Escalation:** Chained with a separate Pyth oracle bug (#42) that makes invalid versions occur on nearly every update, turning a rare event into a near-constant accounting drain.
**Insight:** When settlement logic invalidates and re-anchors positions, check whether global and local state machines can take different paths across the same event; any asymmetry in which oracle version each side settles against breaks PnL accounting even without direct value transfer.
