RustFund

First Flight #36
Beginner FriendlyRust
100 EXP
View results
Submission Details
Severity: medium
Valid

Deadline Can Be Set Multiple Times Due to Missing Flag Update in `FundSetDeadline`

Summary

The "rustfund" contract has a high-severity vulnerability in the FundSetDeadline function, allowing the creator to set the fund’s deadline multiple times. The function includes a check to prevent this using a dealine_set flag (likely a typo for deadline_set), but it fails to update this flag to true after setting the deadline. As a result, the creator can repeatedly modify the deadline, potentially manipulating the crowdfunding timeline to delay refunds or extend the campaign indefinitely. This undermines the contract’s integrity and contributor trust, as a fixed deadline is a standard expectation in crowdfunding systems.


Vulnerability Details

The vulnerability occurs in the FundSetDeadline function, which is intended to allow the creator to set a deadline for the fund only once. However, due to a missing update to the dealine_set flag, the restriction is ineffective, enabling multiple deadline changes.

Root Cause

The root cause is the omission of an instruction to set fund.dealine_set = true after updating fund.deadline. The function checks the flag to prevent re-setting but does not mark the deadline as set, allowing subsequent calls to succeed. Below is the relevant code snippet:

pub fn set_deadline(ctx: Context<FundSetDeadline>, deadline: u64) -> Result<()> {
let fund = &mut ctx.accounts.fund;
if fund.dealine_set {
return Err(ErrorCode::DeadlineAlreadySet.into());
}
fund.deadline = deadline;
Ok(())
}

In this snippet:

  • if fund.dealine_set checks whether the deadline has already been set, intending to block further changes.

  • If the check passes (i.e., dealine_set is false), fund.deadline is updated to the new value.

  • However, there is no subsequent fund.dealine_set = true; to indicate the deadline has been set, so dealine_set remains false (its default value from FundCreate).

  • Note the typo: dealine_set should be deadline_set, as defined in the Fund struct, though this doesn’t affect functionality in this context since the field is consistently misspelled.

The Fund struct confirms the presence of the flag:

#[account]
#[derive(InitSpace)]
pub struct Fund {
#[max_len(200)]
pub name: String,
#[max_len(5000)]
pub description: String,
pub goal: u64,
pub deadline: u64,
pub creator: Pubkey,
pub amount_raised: u64,
pub dealine_set: bool, // Typo here, initialized as false in FundCreate
}

In FundCreate, dealine_set is initialized to false:

pub fn fund_create(ctx: Context<FundCreate>, name: String, description: String, goal: u64) -> Result<()> {
let fund = &mut ctx.accounts.fund;
fund.name = name;
fund.description = description;
fund.goal = goal;
fund.deadline = 0;
fund.creator = ctx.accounts.creator.key();
fund.amount_raised = 0;
fund.dealine_set = false; // Initial state
Ok(())
}

Since FundSetDeadline never updates this flag, it remains false, allowing the creator to call the function repeatedly.


Impact

This vulnerability has significant implications for the contract’s operation and fairness:

  • Timeline Manipulation: The creator can change the deadline multiple times, extending the campaign indefinitely or shortening it to trigger immediate refund eligibility. For example, they could set a distant deadline, collect funds, then reset it to a past date to block refunds while withdrawing funds (exacerbated by the second vulnerability).

  • Interaction with Other Functions:

    • In FundContribute, contributions are blocked if the deadline has passed (deadline < current_time), so extending the deadline allows more contributions.

    • In FundRefund, refunds are only allowed after the deadline, so delaying it prevents contributors from reclaiming funds.


Tools Used

  • Manual Code Review

Recommendations

To address this vulnerability, the following steps are recommended:

  1. Update the dealine_set Flag in FundSetDeadline:
    Modify the FundSetDeadline function to set fund.dealine_set = true after updating the deadline, ensuring it can only be set once. Here’s the corrected code snippet:

    pub fn set_deadline(ctx: Context<FundSetDeadline>, deadline: u64) -> Result<()> {
    let fund = &mut ctx.accounts.fund;
    if fund.dealine_set {
    return Err(ErrorCode::DeadlineAlreadySet.into());
    }
    fund.deadline = deadline;
    fund.dealine_set = true; // Add this line to mark deadline as set
    Ok(())
    }
  2. Fix the Typo:
    Correct the typo from dealine_set to deadline_set for consistency and clarity across the contract. Update all instances:

    • In the Fund struct:

      pub deadline_set: bool, // Corrected from dealine_set
    • In FundCreate:

      fund.deadline_set = false; // Corrected
    • In FundSetDeadline:

      if fund.deadline_set { // Corrected
      return Err(ErrorCode::DeadlineAlreadySet.into());
      }
      fund.deadline_set = true; // Corrected
Updates

Appeal created

bube Lead Judge 3 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Deadline set flag is not updated in `set_deadline` function

Support

FAQs

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