RustFund

First Flight #36
Beginner FriendlyRust
100 EXP
View results
Submission Details
Severity: high
Invalid

Potential Reentrancy Attack Due to State Updates After Transfer

Summary

The contribute function updates the fund state after transferring funds, which opens the possibility for a reentrancy attack if Solana adds custom programs that allow indirect reentrancy.

Vulnerability Details

  • The function first executes the SOL transfer:

system_program::transfer(cpi_context, amount)?;
  • Then, it updates the total raised amount:

fund.amount_raised += amount;
  • If a malicious program is attached to the recipient account, it could execute another function call before fund.amount_raised is updated, leading to inconsistencies.

Impact

  • Fund total raised could be manipulated before finalizing contributions.

  • If Solana allows future program hooks, this can be exploited.

  • Defensive coding practice suggests always updating state before external calls.

Tools Used

Manual Code Review

Recommendations

Final Comprehensive Secured Version of contributeFunction:

pub fn contribute(ctx: Context<FundContribute>, amount: u64) -> Result<()> {
let fund = &mut ctx.accounts.fund;
let contribution = &mut ctx.accounts.contribution;
// ✅ [Fix] Check that the contribution amount is greater than zero to prevent no-op transactions
if amount == 0 {
return Err(ErrorCode::UnauthorizedAccess.into()); // More descriptive error could be used
}
// ✅ [Fix] Safely get the current blockchain timestamp and handle conversion errors
let current_time = Clock::get()
.map_err(|_| ErrorCode::CalculationOverflow)?
.unix_timestamp
.try_into()
.map_err(|_| ErrorCode::CalculationOverflow)?;
// ✅ [Fix] Enforce deadline properly, ensuring past contributions are not allowed
if fund.deadline != 0 && fund.deadline < current_time {
return Err(ErrorCode::DeadlineReached.into());
}
// ✅ [Fix] Initialize the contribution account properly, if it's a new contribution
if contribution.contributor == Pubkey::default() {
contribution.contributor = ctx.accounts.contributor.key();
contribution.fund = fund.key();
contribution.amount = 0; // Initialize contribution amount
}
// ✅ [Fix] Prevent integer overflow when adding contribution amounts
contribution.amount = contribution
.amount
.checked_add(amount)
.ok_or(ErrorCode::CalculationOverflow)?;
// ✅ [Fix] Prevent integer overflow when updating fund's total raised amount
fund.amount_raised = fund
.amount_raised
.checked_add(amount)
.ok_or(ErrorCode::CalculationOverflow)?;
// ✅ [Fix] Ensure the state is updated **before** performing the transfer to avoid reentrancy risks
// Transfer SOL from contributor to fund account
let cpi_context = CpiContext::new(
ctx.accounts.system_program.to_account_info(),
system_program::Transfer {
from: ctx.accounts.contributor.to_account_info(),
to: fund.to_account_info(),
},
);
// Perform the transfer
system_program::transfer(cpi_context, amount)?;
Ok(())
}
Updates

Appeal created

bube Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

[Invalid] Reentrancy

The reentrancy attacks occur when the contract modifies state and makes an external call, allowing the attacker to reenter. The `contribute` function doesn't perform an external call. For the SOL transfer the function uses a system program, not an external call to another smart contract. Therefore, there is no attack vector for reentrancy.

SashaFlores Submitter
6 months ago
bube Lead Judge
6 months ago
bube Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

[Invalid] Reentrancy

The reentrancy attacks occur when the contract modifies state and makes an external call, allowing the attacker to reenter. The `contribute` function doesn't perform an external call. For the SOL transfer the function uses a system program, not an external call to another smart contract. Therefore, there is no attack vector for reentrancy.

Support

FAQs

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