Rust Fund

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

Unchecked amount_raised add, no goal>0 check, panicking Clock unwraps, rent-exemption not preserved

Description

Four low-severity issues in rustfund:

  1. Unchecked arithmetic on amount_raised. contribute does fund.amount_raised += amount; with a raw +=. Every other lamport update in the program uses checked_add / checked_sub; this one relies on overflow-checks = true being set in Cargo.toml. If release builds are compiled without overflow checks, the addition wraps silently. Use checked_add.

  2. No validation that goal > 0. fund_create accepts any goal, including 0. A zero-goal campaign is trivially "successful" (amount_raised >= goal holds with no contributions), which matters once a goal check is added to withdraw. Require goal > 0.

  3. Panicking on Clock / conversion. contribute and refund use Clock::get().unwrap() and .try_into().unwrap(). A failure aborts with a panic instead of returning a clean program error. Use ? with the Result and map to a program error.

  4. Withdraw / refund can drop the Fund PDA below rent-exemption. Both move lamports directly out of the program-owned Fund account via try_borrow_mut_lamports. Neither ensures the account keeps at least the rent-exempt minimum, so the account can become rent-collectable and be purged. Reserve the rent-exempt minimum when computing the transferable amount.

(Cosmetic: the field is misspelled dealine_set — harmless on its own, but it is the flag the dead set-once guard in set_deadline checks.)

Risk

Impact: Low. Item 1 requires overflow-checks to be disabled and an implausibly large sum; 2–4 are correctness / robustness issues that do not by themselves move funds.

Likelihood: Low.

Proof of Concept

// (1) unchecked add:
fund.amount_raised += amount; // vs fund.amount_raised.checked_add(amount)?
// (2) zero goal:
fund_create(ctx, name, desc, 0) // campaign is "met" with zero contributions
// (3) panic path:
Clock::get().unwrap().unix_timestamp.try_into().unwrap() // panics instead of erroring

Recommended Mitigation

  • fund.amount_raised = fund.amount_raised.checked_add(amount).ok_or(ErrorCode::CalculationOverflow)?;

  • require!(goal > 0, ErrorCode::InvalidGoal); in fund_create.

  • Replace .unwrap() on Clock::get() / try_into() with ? and a mapped program error.

  • When paying out, keep the rent-exempt minimum: transfer lamports.saturating_sub(Rent::get()?.minimum_balance(fund.data_len())) at most.

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 2 hours 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!