Rust Fund

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

Infinite Deadline Resets and Eternal Fund Brick Vector

Root + Impact

Description

  • Describe the normal behavior in one or more sentences

  • Explain the specific issue or problem in one or more sentences

The set_deadlie

Risk

Likelihood:

  • Reason 1 // Describe WHEN this will occur (avoid using "if" statements)

  • Reason 2

Impact:

Summary

The set_deadline instruction lacks input validation, allowing a campaign creator to pass a historical timestamp. This instantly and permanently blocks all user contributions, bricking the fund lifecycle.

Vulnerability Detail

The set_deadline function at lib.rs:55-63 processes arbitrary u64 input parameters without checking the cluster runtime clock.

If a creator invokes this method with a past timestamp (e.g., 1), the logic loop inside the contribute instruction evaluates the current network time against this historical constraint. The transaction immediately fails the time window verification, rejecting all subsequent deposits and freezing the crowdfunding campaign state irreversibly.

Impact

  • Permanent Campaign Denial: Valid user contributions are blocked indefinitely.

  • Irreversible State Lock: Operational state data and base funding cannot be recovered or moved because the state criteria are broken.

Proof of Concept

The exploit path is completely deterministic. The set_deadline function accepts any u64 with zero validation against the current clock. The contribute function compares the stored deadline against Clock::get() using a strict less-than check. A past deadline makes this comparison permanently true. The fund is bricked with no recovery. The creator cannot fix it because dealine_set is never flipped to true (separate finding), enabling infinite deadline resets—but even setting a future deadline later is impossible if the flag were fixed, making the bricking irreversible.

The automated execution sequence initializes a standard crowdfunding target state, sets the deadline variable to the Unix epoch base value (1), and demonstrates that subsequent deposit instructions are rejected by the runtime:

it("past deadline bricks the fund - no contributions possible", async () => {
// Step 1: Create a normal fund with a target amount using the program's createFund instruction
await program.methods.createFund(new anchor.BN(100 * LAMPORTS_PER_SOL))
.accounts({ creator: creator.publicKey, fund: fundPDA })
.signers([creator]).rpc();
// Step 2: Creator sets the deadline to Unix timestamp 1 (January 1, 1970)
// This is a valid u64 but far in the past. No validation blocks it.
await program.methods.setDeadline(new anchor.BN(1))
.accounts({ creator: creator.publicKey, fund: fundPDA })
.signers([creator]).rpc();
// Step 3: Any contribution now hits the deadline check at lib.rs:
// if fund.deadline != 0 && fund.deadline < Clock::get()?.unix_timestamp as u64
// 1 != 0 evaluates to true, and 1 < current_timestamp evaluates to true.
// The expression evaluates to: true && true -> triggers a revert with DeadlineReached.
try {
await program.methods.contribute(new anchor.BN(1 * LAMPORTS_PER_SOL))
.accounts({ contributor: contributor.publicKey, fund: fundPDA })
.signers([contributor]).rpc();
assert.fail("Should have reverted with DeadlineReached");
} catch (err: any) {
// Assert that the transaction fails explicitly due to the past deadline validation logic
assert(err.toString().includes("DeadlineReached") || err.logs.toString().includes("DeadlineReached"));
}
});

Mitigation

Add a validation check in set_deadline that rejects any deadline earlier than the current blockchain timestamp. This prevents the creator from ever setting a past deadline and bricking the fund. This fix complements the dealine_set flag fix from the separate finding. Together, they ensure the deadline can be set exactly once to a meaningful future value, preventing both infinite resets and permanent bricking.

Modify the instruction handler and error configurations as shown below:

pub fn set_deadline(ctx: Context<FundSetDeadline>, deadline: u64) -> Result<()> {
let fund = &mut ctx.accounts.fund;
// Add this validation: deadline must be in the future
let current_time = Clock::get()?.unix_timestamp as u64;
require!(deadline > current_time, ErrorCode::DeadlineInPast);
if fund.dealine_set {
return Err(ErrorCode::DeadlineAlreadySet.into());
}
fund.deadline = deadline;
fund.dealine_set = true;
Ok(())
}

Add the corresponding error variant to the ErrorCode enum:

#[error_code]
pub enum ErrorCode {
// ... existing errors ...
DeadlineInPast, // Add this line
}

Recommended Mitigation

- remove this code
+ add this code
Updates

Lead Judging Commences

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