RustFund

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

M-02: Ineffective Deadline State Protection Allows Multiple Deadline Changes

M-02: Ineffective Deadline State Protection Allows Multiple Deadline Changes

Severity: Medium
Category: State Management / Input Validation

Summary

The RustFund protocol includes a dealineSet flag intended to restrict deadline modifications to a single instance. However, this flag is never updated within the setDeadline function, rendering it ineffective. Consequently, fund creators can repeatedly change campaign deadlines.

Vulnerability Details

Root cause:
The setDeadline function fails to set the dealineSet boolean flag after the initial deadline assignment. Although the flag is checked at the start of the function (if fund.dealineSet), the flag remains permanently set to false.

Vulnerable Component:

  • File: lib.rs

  • Function: setDeadline

  • Struct: Fund

Impact:

  • Fund creators can arbitrarily adjust deadlines multiple times, undermining the integrity of fixed campaign timelines.

  • Contributors lose certainty around campaign deadlines, negatively impacting decision-making and trust.

  • Protocol guarantees regarding deadline immutability are not enforced, violating core design principles.

Steps to Reproduce

  1. Create a new fund.

  2. Call setDeadline once to establish the initial deadline.

  3. Inspect the dealineSet flag, verifying it remains false.

  4. Successfully call setDeadline again to modify the deadline.

Proof of Concept

// Create fund
await program.methods
.fundCreate(FUND_NAME, "State validation test", new anchor.BN(5 * LAMPORTS_PER_SOL))
.accounts({ fund, creator: creator.publicKey, systemProgram: SystemProgram.programId })
.signers([creator])
.rpc();
// Check initial dealineSet flag
const fundDataBefore = await program.account.fund.fetch(fund);
console.log("Initial fund.dealineSet value:", fundDataBefore.dealineSet);
// Set deadline first time
await program.methods
.setDeadline(new anchor.BN(Math.floor(Date.now() / 1000) + 86400))
.accounts({ fund, creator: creator.publicKey })
.signers([creator])
.rpc();
// Check if dealineSet flag was updated
const fundDataAfter = await program.account.fund.fetch(fund);
console.log("After first deadline set, fund.dealineSet value:", fundDataAfter.dealineSet);
// Set deadline second time (should fail but succeeds)
await program.methods
.setDeadline(new anchor.BN(Math.floor(Date.now() / 1000) + 172800))
.accounts({ fund, creator: creator.publicKey })
.signers([creator])
.rpc();
// Verify final state
const fundDataFinal = await program.account.fund.fetch(fund);
console.log("Final deadline timestamp:", fundDataFinal.deadline.toString());
console.log("Final dealineSet value:", fundDataFinal.dealineSet);
/* OUTPUT:
Initial fund.dealineSet value: false
First deadline set successfully
After first deadline set, fund.dealineSet value: false
VULNERABILITY: Could set deadline multiple times despite 'dealineSet' flag
Final deadline timestamp: 1743015396
Final dealineSet value: false
*/

Tools Used

  • Manual code review

  • Anchor test framework

Recommended Mitigation

Update the setDeadline function to properly set the dealineSet flag upon the first deadline assignment:

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

Additionally, define the corresponding error code clearly:

#[error_code]
pub enum ErrorCode {
// existing errors...
#[msg("Deadline has already been set")]
DeadlineAlreadySet,
}
Updates

Appeal created

bube Lead Judge 5 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.