Skip to content

Security Audit: Fix oracle staleness bypass, div-by-zero, and deserialization panics#2115

Open
AdeshAtole wants to merge 1 commit intodrift-labs:masterfrom
AdeshAtole:fix/security-audit-findings
Open

Security Audit: Fix oracle staleness bypass, div-by-zero, and deserialization panics#2115
AdeshAtole wants to merge 1 commit intodrift-labs:masterfrom
AdeshAtole:fix/security-audit-findings

Conversation

@AdeshAtole
Copy link

@AdeshAtole AdeshAtole commented Feb 12, 2026

Security Audit Findings

Finding 1: CRITICAL — Prelaunch Oracle Delay Inverted (Always Reports Fresh)

File: programs/drift/src/state/oracle.rs:632
Severity: Critical

The get_prelaunch_price() function computes delay as:

delay: oracle.amm_last_update_slot.saturating_sub(slot).cast()?

Since slot (current) is always >= amm_last_update_slot (past), saturating_sub returns 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:

  • Execute trades at outdated prices
  • Avoid liquidation (stale oracle wont trigger margin violations)
  • Manipulate funding rates (oracle staleness checks pass)

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,585
Severity: High

get_sb_on_demand_price() divides by latest_oracle_submssions.len() and indexes [0] without checking for empty submissions. If latest_submissions() returns an empty vec:

  • Line 571: division by zero → program panic
  • Line 585: index out of bounds → program panic

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 UnableToLoadOracle error when submissions are empty.


Finding 3: MEDIUM — Unchecked unwrap() on Oracle Deserialization

File: programs/drift/src/state/oracle.rs:422,454
Severity: Medium

PriceUpdateV2::try_deserialize() and PythLazerOracle::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::Liquidate in math/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_offset adds oracle_price_twap.abs() / FUNDING_RATE_OFFSET_DENOMINATOR unconditionally (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

    • Enhanced error handling for oracle data loading failures, providing explicit error messages when oracle deserialization fails.
    • Added validation to prevent processing when oracle submission data is unavailable.
  • Changes

    • Modified delay calculation methodology for prelaunch asset pricing to ensure accurate pricing computations.

…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
@coderabbitai
Copy link

coderabbitai bot commented Feb 12, 2026

Walkthrough

Improves 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

Cohort / File(s) Summary
Oracle Error Handling & Logic
programs/drift/src/state/oracle.rs
Replaces unwrap() with map_err(ErrorCode::UnableToLoadOracle) in get_pyth_price and get_prelaunch_price for improved error propagation; adds guard to return error when latest_oracle_submissions is empty in get_sb_on_demand_price; swaps operand order in delay calculation from amm_last_update_slot.saturating_sub(slot) to slot.saturating_sub(amm_last_update_slot) in get_prelaunch_price.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

A rabbit hops through oracle paths so grand,
Error-handling now flows across the land,
No more unwraps that stumble and fall,
Prelaunch delays get calculated right—that's all! 🐰✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly and specifically summarizes the main changes: fixing oracle staleness bypass, div-by-zero, and deserialization panics—all critical security issues addressed in the changeset.

✏️ 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant