Rust Fund

AI First Flight #9
Beginner FriendlyRust
EXP
View results
Submission Details
Severity: low
Valid

`withdraw`/`refund` mutate lamports directly with no rent-exemption check — the fund PDA can be left below rent minimum and purged

Description

withdraw and refund move SOL by mutating raw account lamports directly (try_borrow_mut_lamports), subtracting from the fund PDA's total lamports(). Neither checks that the fund account retains its rent-exempt minimum afterward.

The fund PDA's balance is rent_reserve + contributed_lamports. withdraw subtracts amount_raised; refund subtracts a contribution.amount. Because the subtraction is against the full balance and there is no rent_exempt_minimum floor, a withdrawal/refund path can bring the account's lamports below the rent-exempt threshold.

**ctx.accounts.fund.to_account_info().try_borrow_mut_lamports()? =
ctx.accounts.fund.to_account_info().lamports() // @> full balance, incl. rent reserve
.checked_sub(amount) // @> no rent-exempt floor enforced
.ok_or(ProgramError::InsufficientFunds)?;

Risk

Likelihood: Low

  • Requires a state where the moved amount eats into the rent reserve (e.g. interaction with the amount_raised desync / double-pay issues, or dust accounting). Not the common path, but reachable.

Impact: Low

  • If the fund account drops below rent-exemption, the runtime can garbage-collect it, destroying the campaign's remaining state (contributions, metadata). Direct lamport manipulation without a rent floor is fragile and can brick a live fund.

Proof of Concept

// Account holds rent_reserve + 1 SOL contributed; amount_raised inflated to > 1 SOL by a
// prior refund that never decremented amount_raised (see the amount_raised desync finding).
withdraw(ctx_creator);
// subtracts an amount that reaches into rent_reserve -> fund PDA left below rent-exempt -> reaped.

Recommended Mitigation

Enforce a rent-exempt floor before any direct lamport decrement:

let rent = Rent::get()?.minimum_balance(fund_ai.data_len());
require!(fund_ai.lamports().saturating_sub(amount) >= rent, ErrorCode::WouldBreakRentExemption);
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 24 minutes ago
Submission Judgement Published
Validated
Assigned finding tags:

[L-03] Unsafe Direct Lamport Manipulation in refund(), withdraw() Functions

## Description The `refund` function in the provided code directly manipulates the lamports of accounts using `try_borrow_mut_lamports()`. This approach bypasses the Solana runtime's safety checks, leading to potential security vulnerabilities and program instability. ## Vulnerability Details In the `refund` function, lamports are transferred between accounts by directly adjusting their balances:   ```Rust **ctx.accounts.fund.to_account_info().try_borrow_mut_lamports()? = ctx.accounts.fund.to_account_info().lamports().checked_sub(amount).ok_or(ProgramError::InsufficientFunds)?; **ctx.accounts.contributor.to_account_info().try_borrow_mut_lamports()? = ctx.accounts.contributor.to_account_info().lamports().checked_add(amount).ok_or(ErrorCode::CalculationOverflow)?; ``` This method of direct lamport manipulation can lead to several issues: 1. **Bypassing Rent Exemption Checks:** Accounts in Solana must maintain a minimum balance to be rent-exempt. Directly reducing an account's lamports without verifying rent exemption can result in the account being marked for deletion by the Solana runtime. 2. **Ownership Constraints:** Only the owning program of an account can modify its data and lamport balance. Direct manipulation without proper checks can violate these constraints, leading to program errors. 3. **Lack of Atomicity:** Direct lamport transfers lack the atomic transaction guarantees provided by the system program's transfer instruction, potentially leading to inconsistent states in case of program interruptions. ## Impact Exploiting this vulnerability can result in unauthorized fund transfers, violation of Solana's account ownership rules, and potential loss of funds due to accounts becoming non-rent-exempt. ## Recommendations Replace the direct lamport manipulation with Solana's system program transfer instruction to ensure safe and compliant fund transfers in refund() & withdraw() functions:   ```Rust let cpi_context = CpiContext::new( ctx.accounts.system_program.to_account_info(), system_program::Transfer { from: ctx.accounts.fund.to_account_info(), to: ctx.accounts.contributor.to_account_info(), }, ); system_program::transfer(cpi_context, amount)?; ``` This approach leverages Solana's native mechanisms for transferring lamports, ensuring adherence to the platform's safety and security protocols.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!