Rust Fund

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

Deadline stored as u64, compared against i64 clock — type mismatch and .unwrap() panics

Root + Impact

Description

The Fund struct stores deadline as u64. All three instructions compare it against Clock::get().unwrap().unix_timestamp.try_into().unwrap(), converting i64 to u64. For timestamps above i64::MAX this panics. More critically, if a client passes a negative i64 value (e.g. from a timestamp handling bug), the u64 interpretation via two's complement becomes u64::MAX, making the deadline appear billions of seconds in the future and permanently blocking refunds. Additionally, every .unwrap() on Clock::get() panics the transaction rather than returning a clean program error.

// Fund struct
pub deadline: u64, // stored unsigned
// contribute()
if fund.deadline != 0 &&
@> fund.deadline < Clock::get().unwrap().unix_timestamp.try_into().unwrap() {
return Err(ErrorCode::DeadlineReached.into());
}
// refund()
if ctx.accounts.fund.deadline != 0 &&
@> ctx.accounts.fund.deadline > Clock::get().unwrap().unix_timestamp.try_into().unwrap() {
return Err(ErrorCode::DeadlineNotReached.into());
}

Risk

Likelihood:

  • Any client that uses Solana's native i64 timestamp type and passes it without explicit casting to u64 produces a two's-complement large value for negative inputs, making the deadline check always false

  • The .unwrap() on Clock::get() in every deadline-touching instruction panics instead of propagating a program error, producing opaque transaction failures

Impact:

  • A deadline stored as a large u64 due to sign-extension never expires, permanently preventing refunds from ever being claimed

  • Panics from .unwrap() on syscalls halt entire instructions with no meaningful on-chain error code

Proof of Concept

// Client-side TypeScript — accidental negative timestamp
const deadline = new BN(-1);
// Serialised as u64 = 18446744073709551615 (u64::MAX)
// Stored in fund.deadline
// In-program comparison:
// 18446744073709551615 < ~1_700_000_000 => false => no DeadlineReached error
// Contributions accepted forever, refunds permanently blocked

Recommended Mitigation

// Fund struct
- pub deadline: u64,
+ pub deadline: i64,
// set_deadline parameter
- deadline: u64
+ deadline: i64
// contribute()
- fund.deadline < Clock::get().unwrap().unix_timestamp.try_into().unwrap()
+ fund.deadline < Clock::get()?.unix_timestamp
// refund()
- ctx.accounts.fund.deadline > Clock::get().unwrap().unix_timestamp.try_into().unwrap()
+ ctx.accounts.fund.deadline > Clock::get()?.unix_timestamp
// set_deadline — require future timestamp
+ require!(deadline > Clock::get()?.unix_timestamp, ErrorCode::InvalidDeadline);
Updates

Lead Judging Commences

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