From e86e359246af6ca0c1ea3daadcfc52ef417b7415 Mon Sep 17 00:00:00 2001 From: IVAN Date: Fri, 26 Jun 2026 20:00:50 +0100 Subject: [PATCH] fix: cap cool_down_period_s to prevent unstake DoS and add missing event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SetUpdateCoolDownPeriod accepted any non-negative i64, including i64::MAX. When unstake later computed `current_unix_timestamp.checked_add(cool_down_period_s)` the addition overflowed, returning CoolDownOverflow for every caller — permanently bricking unstake for all users (issue #39). Fix: - Reject values above MAX_COOL_DOWN_PERIOD_S (10 years, ~315_360_000 s) in set.rs alongside the existing negative-value guard. - Emit a new CoolDownPeriodUpdated event so off-chain monitors can detect authority changes to the cooldown without polling state directly. Co-Authored-By: Claude Sonnet 4.6 --- solana-program/src/error.rs | 2 +- solana-program/src/event.rs | 4 ++++ solana-program/src/instructions/set.rs | 11 ++++++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/solana-program/src/error.rs b/solana-program/src/error.rs index e28c3cf..b040af9 100644 --- a/solana-program/src/error.rs +++ b/solana-program/src/error.rs @@ -40,7 +40,7 @@ pub enum ErrorCode { #[error("Event serialization failed")] EmitEventError = 6012, // 0x177C - #[error("Invalid cooldown period: must be non-negative")] + #[error("Invalid cooldown period: must be non-negative and at most MAX_COOL_DOWN_PERIOD_S")] InvalidCoolDownPeriod = 6013, // 0x177D #[error("Stake amount too small to mint any xORCA")] diff --git a/solana-program/src/event.rs b/solana-program/src/event.rs index d1e0e98..ff740fb 100644 --- a/solana-program/src/event.rs +++ b/solana-program/src/event.rs @@ -31,6 +31,10 @@ pub enum Event<'a> { new_authority: &'a Pubkey, set_by: &'a Pubkey, }, + CoolDownPeriodUpdated { + new_cool_down_period_s: &'a i64, + set_by: &'a Pubkey, + }, } pub fn sol_log_data(data: &[&[u8]]) { diff --git a/solana-program/src/instructions/set.rs b/solana-program/src/instructions/set.rs index 5192b4c..6a2490c 100644 --- a/solana-program/src/instructions/set.rs +++ b/solana-program/src/instructions/set.rs @@ -38,10 +38,19 @@ pub fn process_instruction( StateUpdateInstruction::UpdateCoolDownPeriod { new_cool_down_period_s, } => { - if *new_cool_down_period_s < 0 { + // Reject negative values and values large enough to overflow the timestamp + // addition in `unstake` (current_unix_timestamp + cool_down_period_s). + // Cap at 10 years so that checked_add in `unstake` can never overflow. + const MAX_COOL_DOWN_PERIOD_S: i64 = 10 * 365 * 24 * 60 * 60; // ~315_360_000 s + if *new_cool_down_period_s < 0 || *new_cool_down_period_s > MAX_COOL_DOWN_PERIOD_S { return Err(ErrorCode::InvalidCoolDownPeriod.into()); } state_view.cool_down_period_s = *new_cool_down_period_s; + Event::CoolDownPeriodUpdated { + new_cool_down_period_s, + set_by: update_authority_account.key(), + } + .emit()?; } StateUpdateInstruction::UpdateUpdateAuthority { new_authority } => { let old_authority = state_view.update_authority;