Skip to content

[BUG BOUNTY] DcaModule: Execution Schedule Drift, Unvalidated Zero/Negative Funding, Arithmetic Overflows, and Storage List DoS #971

Description

@littfed

Description & Vulnerability Analysis

A comprehensive security audit of contracts/dca-module/src/lib.rs identified several critical and high-severity flaws that break the DCA execution cadence, allow creation of dead unexecutable plans with arbitrary invalid funding, risk arithmetic overflow panics, and expose the protocol to ledger storage exhaustion attacks:

1. Permanent Execution Schedule Drift (Schedule Invariant Failure)

In execute:

let current_ledger = env.ledger().sequence() as u64;
...
plan.next_execution_ledger = current_ledger + Self::frequency_to_ledgers(&plan.frequency);
  • Mechanism: In dollar-cost averaging protocols, executions are meant to occur periodically according to a fixed cadence ($T_0 + N \times \text{interval}$). However, automated keeper bots or off-chain executors rarely execute at the exact block when a transaction becomes due (e.g., due to transaction batching, gas pricing, or network latency).
  • When a keeper executes at current_ledger = plan.next_execution_ledger + \Delta, recalculating next_execution_ledger = current_ledger + interval permanently offsets future executions by $\Delta$.
  • Over time, this cumulative schedule drift causes user plans to execute significantly fewer times than intended, distorting the dollar-cost average pricing and breaking scheduled automation.
  • Fix: Anchor the schedule to the planned cadence (plan.next_execution_ledger = plan.next_execution_ledger.saturating_add(interval)), while only fast-forwarding to current_ledger.saturating_add(interval) if execution experienced severe keeper delay ($\ge \text{interval}$).

2. Unvalidated funded_amount in create_plan

In create_plan:

  • funded_amount was never checked against amount_per_execution or for positivity:
    • A user could supply funded_amount <= 0 (or negative).
    • A user could supply funded_amount < amount_per_execution.
  • Plans with inadequate or negative funding were created and persisted into contract state, but subsequent calls to execute() fail immediately with InsufficientFunds, creating permanently dead/zombie plans that clutter contract state and user queries.
  • Fix: Validate that funded_amount > 0 and funded_amount >= amount_per_execution, rejecting invalid plans at initialization.

3. Unchecked Arithmetic in execute and cancel

  • In execute: let remaining = plan.funded_amount - plan.spent_amount; and plan.spent_amount += plan.amount_per_execution;
  • In cancel: let refund = plan.funded_amount - plan.spent_amount;
  • Unchecked subtraction and addition risk integer underflow/overflow panics in Rust.
  • Fix: Employ checked_sub and checked_add throughout all accounting operations.

4. Unbounded User Plans Vector (Storage Exhaustion DoS)

In create_plan:

let mut user_plans = Self::get_user_plans_internal(&env, &owner);
user_plans.push_back(plan_id);
Self::set_user_plans(&env, &owner, &user_plans);
  • A user can repeatedly create plans, monotonically growing the Vec<u64> entry in persistent storage.
  • Soroban limits persistent ledger entries to 64 KB. Once the entry grows too large, serialization/deserialization CPU/RAM costs exceed transaction limits or exceed entry limits, rendering create_plan and get_user_plans unusable for that account.
  • Fix: Enforce a maximum plan limit per account (MAX_USER_PLANS = 100) returning DcaError::MaxPlansReached.

5. Deprecated Event API & Missing Unit Test Suite

  • Replaced legacy env.events().publish with typed #[contractevent] structs (DcaPlanCreatedEvent, DcaExecutedEvent, DcaPausedEvent, DcaResumedEvent, DcaCancelledEvent), eliminating deprecation warnings.
  • Wrote a 12-test unit testing suite in contracts/dca-module/src/test.rs covering all execution paths, anti-drift timing invariants, refund accounting, validations, and storage limits.

Bounty Payout Information

  • Bug Bounty Program: StellarLend Security Bug Bounty (SECURITY.md)
  • Severity Assessment: High / Medium (Protocol scheduling disruption, unvalidated fund accounting, storage DoS)
  • Researcher: teddyvj <teddy.vj@gmail.com>
  • Payout Addresses:
    • EVM (USDC / USDT): 0xf5fcb1f90f8a2e658f38f72f0156ecbec7aa964d
    • Solana (USDC / SOL): ECYrMmKpVNyvWMuLNkBuWxqkk2TNRC3qVPDgonBwamKP
    • Bitcoin (BTC): bc1qjg5lug59rn9rz2j2f9ut798g99mn2asw9jfv0s

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions