Rust Fund

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

refund() and withdraw() use direct lamport manipulation instead of system_program::transfer, bypassing Solana runtime safety checks

Root + Impact

Description

  • Both refund() and withdraw() transfer SOL by directly mutating account lamport fields via try_borrow_mut_lamports(). This bypasses the Solana runtime's ownership checks, rent-exemption enforcement, and atomic transfer guarantees.

// refund() and withdraw() — same pattern in both:
**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)?;

Risk

Likelihood: Every call to refund() or withdraw() uses this pattern — 100% of SOL transfer operations in the program.

Impact: Direct lamport manipulation can reduce the fund PDA below its rent-exempt minimum, causing the Solana runtime to mark it for deletion and permanently locking any remaining balance. It also violates program ownership constraints and lacks the atomicity guarantees of system_program::transfer.

Proof of Concept

grep -n "try_borrow_mut_lamports" programs/rustfund/src/lib.rs
73: **ctx.accounts.fund.to_account_info().try_borrow_mut_lamports()? =
78: **ctx.accounts.contributor.to_account_info().try_borrow_mut_lamports()? =
95: **ctx.accounts.fund.to_account_info().try_borrow_mut_lamports()? =
100: **ctx.accounts.creator.to_account_info().try_borrow_mut_lamports()? =

system_program::transfer is already used correctly in contribute() (line 46) — the same pattern should be applied to both outbound transfer functions.

Recommended Mitigation

Replace direct lamport mutation with system_program::transfer in both refund() and withdraw():

- **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)?;
+ let cpi_ctx = 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_ctx, amount)?;
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 21 days 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!