Security Audit: Fix oracle staleness bypass, div-by-zero, and deserialization panics#2115
Security Audit: Fix oracle staleness bypass, div-by-zero, and deserialization panics#2115AdeshAtole wants to merge 1 commit intodrift-labs:masterfrom
Conversation
…panics 1. CRITICAL: Prelaunch oracle delay calculation was inverted (amm_last_update_slot - slot instead of slot - amm_last_update_slot), making oracle always appear fresh (delay=0) 2. HIGH: Switchboard on-demand division by zero when latest_submissions() returns empty vec 3. MEDIUM: Replace unwrap() with proper error handling in Pyth Pull and Pyth Lazer oracle deserialization to prevent program panics from malformed oracle data
WalkthroughImproves oracle data handling in the Drift program by replacing unwrap() calls with explicit error propagation for Pyth oracle deserialization failures, adds a guard for empty oracle submissions, and modifies the delay calculation logic in prelaunch price computation. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. No actionable comments were generated in the recent review. 🎉 Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Security Audit Findings
Finding 1: CRITICAL — Prelaunch Oracle Delay Inverted (Always Reports Fresh)
File:
programs/drift/src/state/oracle.rs:632Severity: Critical
The
get_prelaunch_price()function computes delay as:Since
slot(current) is always >=amm_last_update_slot(past),saturating_subreturns 0. The oracle always appears fresh regardless of actual staleness.Attack Scenario: An attacker can use a prelaunch market with an arbitrarily stale oracle to:
Fix: Swap operands to
slot.saturating_sub(oracle.amm_last_update_slot)Finding 2: HIGH — Switchboard On-Demand Division by Zero
File:
programs/drift/src/state/oracle.rs:571,585Severity: High
get_sb_on_demand_price()divides bylatest_oracle_submssions.len()and indexes[0]without checking for empty submissions. Iflatest_submissions()returns an empty vec:Attack Scenario: If a Switchboard on-demand feed has no recent submissions (stale/deactivated feed), any instruction touching that oracle will panic, potentially DOSing liquidations or settlements for affected markets.
Fix: Early return with
UnableToLoadOracleerror when submissions are empty.Finding 3: MEDIUM — Unchecked
unwrap()on Oracle DeserializationFile:
programs/drift/src/state/oracle.rs:422,454Severity: Medium
PriceUpdateV2::try_deserialize()andPythLazerOracle::try_deserialize()use.unwrap(). A malformed oracle account passed to these functions would panic the program.Attack Scenario: While oracle accounts are validated by the account constraint system, a corrupted or incorrectly initialized account could still reach deserialization. The panic would halt the transaction, potentially DOSing any instruction path that reads this oracle.
Fix: Replace
.unwrap()with.map_err(|_| ErrorCode::UnableToLoadOracle)?Additional Observations (Informational)
Liquidation allows TooUncertain oracles (
DriftAction::Liquidateinmath/oracle.rs:209): Liquidations proceed even when oracle confidence is very wide. While twap divergence checks mitigate this, highly speculative markets (50x confidence multiplier) could see liquidations at unreliable prices.Oracle normalisation toward mark price (
math/amm.rs:770-785): The normalise_oracle_price function biases oracle toward reserve price using confidence interval. During oracle manipulation attacks, a manipulated mark price would pull the "normalised" oracle price toward the manipulated value, amplifying the attack.Funding rate offset always positive (
controller/funding.rs:231):price_spread_with_offsetaddsoracle_price_twap.abs() / FUNDING_RATE_OFFSET_DENOMINATORunconditionally (always positive), creating a slight bias toward longs paying shorts regardless of market conditions. This is likely intentional (to incentivize shorting) but worth noting.Summary by CodeRabbit
Bug Fixes
Changes