Rust Fund

AI First Flight #9
Beginner FriendlyRust
EXP
View results
Submission Details
Impact: low
Likelihood: low
Invalid

Missing Rent Exemption Handling

Root + Impact

Description

  • Describe the normal behavior in one or more sentences
    When refunding or withdrawing funds, the code doesn't explicitly handle rent exemption for the fund account. If all funds are withdrawn and the account balance falls below the rent exemption threshold, the account could potentially be closed, losing the fund's state data.

  • Explain the specific issue or problem in one or more sentences
    The normal behavior should ensure that when withdrawing or refunding, the fund account maintains sufficient balance for rent exemption. The current implementation doesn't account for this.

```rust:90-105:programs/rustfund/src/lib.rs
pub fn withdraw(ctx: Context<FundWithdraw>) -> Result<()> {
let amount = ctx.accounts.fund.amount_raised;
**ctx.accounts.fund.to_account_info().try_borrow_mut_lamports()? =
ctx.accounts.fund.to_account_info().lamports()
.checked_sub(amount)
.ok_or(ProgramError::InsufficientFunds)?;
```
If `amount_raised` equals the total account balance (including rent), withdrawing it all would leave the account with 0 lamports, making it rent-exempt and closable.

Risk

Likelihood:

  • * This occurs when fund account balance exactly equals amount_raised + rent

    * Happens when all contributions exactly match the account's total balance

    * Rare but possible edge case

Impact:

  • * Fund account could become rent-exempt after withdrawal

    * Account state could be lost if account is closed

    * Historical data about the fund would be lost

    * Potential for account closure attacks

Proof of Concept

1. Fund account requires 2,000,000 lamports for rent exemption
2. Campaign raises exactly 1 SOL (1,000,000,000 lamports)
3. Account balance = 1,002,000,000 lamports (rent + raised)
4. Creator withdraws `amount_raised` (1,000,000,000)
5. Account balance = 2,000,000 (exactly rent exemption)
6. Account is now rent-exempt and can be closed

Recommended Mitigation

```diff
pub fn withdraw(ctx: Context<FundWithdraw>) -> Result<()> {
+ if ctx.accounts.fund.amount_raised < ctx.accounts.fund.goal {
+ return Err(ErrorCode::GoalNotMet.into());
+ }
+
let amount = ctx.accounts.fund.amount_raised;
+
+ if amount == 0 {
+ return Err(ErrorCode::AlreadyWithdrawn.into());
+ }
+
+ // Ensure account maintains rent exemption
+ let rent = Rent::get()?.minimum_balance(ctx.accounts.fund.to_account_info().data_len());
+ let current_balance = ctx.accounts.fund.to_account_info().lamports();
+ let withdrawable_amount = current_balance.checked_sub(rent)
+ .ok_or(ErrorCode::InsufficientFundsForRent)?;
+
+ // Only withdraw up to the withdrawable amount
+ let withdraw_amount = amount.min(withdrawable_amount);
- **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.creator.to_account_info().try_borrow_mut_lamports()? =
- ctx.accounts.creator.to_account_info().lamports()
- .checked_add(amount)
- .ok_or(ErrorCode::CalculationOverflow)?;
+ let cpi_context = CpiContext::new_with_signer(
+ ctx.accounts.system_program.to_account_info(),
+ system_program::Transfer {
+ from: ctx.accounts.fund.to_account_info(),
+ to: ctx.accounts.creator.to_account_info(),
+ },
+ &[&[
+ ctx.accounts.fund.name.as_bytes(),
+ ctx.accounts.fund.creator.as_ref(),
+ &[ctx.bumps.get("fund").unwrap()],
+ ]],
+ );
+ system_program::transfer(cpi_context, withdraw_amount)?;
+ ctx.accounts.fund.amount_raised = 0;
Ok(())
}
```
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 16 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!