Emergency Pause
Detects missing or vulnerable emergency pause mechanisms in Solana programs.
Emergency Pause
Overview
Remediation Guide: How to Fix Emergency Pause Issues
The emergency pause detector identifies Solana programs that lack an emergency stop mechanism or have flawed pause implementations. It flags programs with critical operations (transfers, state changes) but no pause check, pause state modifications without authority verification, and critical operations that bypass existing pause checks. An emergency pause mechanism allows program operators to halt sensitive operations when a vulnerability is discovered, giving users time to withdraw funds before a fix is deployed.
Why This Is an Issue
Without an emergency pause mechanism, a program has no circuit breaker:
- No incident response (CWE-269): When a vulnerability is discovered, there is no way to stop exploitation while a fix is prepared and deployed. Users remain at risk for the entire duration.
- Unvalidated pause authority (CWE-284): If anyone can toggle the pause state, an attacker can grief the protocol by pausing it or unpause a legitimately paused program to continue exploiting it.
- Pause bypass: Critical operations that do not check the pause state render the mechanism ineffective. Even with a pause implementation, unchecked paths allow continued exploitation.
Emergency pause is considered a best practice for any program that manages user funds.
How to Resolve
Native Solana
use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey};
pub fn transfer_funds(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
let authority = &accounts[0];
let vault = &accounts[1];
let dest = &accounts[2];
let pause_state = &accounts[3];
// Check pause state BEFORE any operation
let pause_data = pause_state.try_borrow_data()?;
if pause_data[0] == 1 {
return Err(ProgramError::InvalidInstructionData); // Program is paused
}
drop(pause_data);
if !authority.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
**vault.try_borrow_mut_lamports()? -= amount;
**dest.try_borrow_mut_lamports()? += amount;
Ok(())
}
pub fn toggle_pause(accounts: &[AccountInfo]) -> ProgramResult {
let pause_authority = &accounts[0];
let pause_state = &accounts[1];
let config = &accounts[2];
// Verify pause authority
if !pause_authority.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
let config_data = config.try_borrow_data()?;
let expected = Pubkey::try_from(&config_data[0..32])
.map_err(|_| ProgramError::InvalidAccountData)?;
if *pause_authority.key != expected {
return Err(ProgramError::InvalidArgument);
}
drop(config_data);
let mut data = pause_state.try_borrow_mut_data()?;
data[0] = if data[0] == 0 { 1 } else { 0 };
Ok(())
}
Anchor
#[derive(Accounts)]
pub struct Transfer<'info> {
pub authority: Signer<'info>,
#[account(constraint = !pause_state.is_paused @ ErrorCode::ProgramPaused)]
pub pause_state: Account<'info, PauseState>,
#[account(mut)]
pub vault: Account<'info, Vault>,
}
#[derive(Accounts)]
pub struct TogglePause<'info> {
pub pause_authority: Signer<'info>,
#[account(
mut,
has_one = pause_authority @ ErrorCode::Unauthorized
)]
pub pause_state: Account<'info, PauseState>,
}
#[account]
pub struct PauseState {
pub is_paused: bool,
pub pause_authority: Pubkey,
}
Examples
Vulnerable Code
pub fn transfer(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
let vault = &accounts[0];
let dest = &accounts[1];
// VULNERABLE: no pause check, no way to stop in emergency
**vault.try_borrow_mut_lamports()? -= amount;
**dest.try_borrow_mut_lamports()? += amount;
Ok(())
}
Fixed Code
pub fn transfer(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
let authority = &accounts[0];
let vault = &accounts[1];
let dest = &accounts[2];
let pause_state = &accounts[3];
// Check pause state first
let pause_data = pause_state.try_borrow_data()?;
if pause_data[0] == 1 {
return Err(ProgramError::InvalidInstructionData);
}
drop(pause_data);
if !authority.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
**vault.try_borrow_mut_lamports()? -= amount;
**dest.try_borrow_mut_lamports()? += amount;
Ok(())
}
Sample Sigvex Output
{
"detector_id": "emergency-pause",
"severity": "medium",
"confidence": 0.75,
"title": "No Emergency Pause Mechanism",
"description": "Critical operations exist but there is no emergency pause functionality. If a vulnerability is discovered, there's no way to stop the program. Users are at risk until a patch can be deployed.",
"location": { "function": "transfer_funds", "block": 0, "statement": 0 },
"cwe": 269
}
Detection Methodology
The detector analyzes pause-related patterns in the function’s intermediate representation:
- Pause state tracking: Identifies binary operations and parameter-range variable reads that represent pause state checks.
- Pause authority identification: Tracks
CheckKeyandCheckOwnerstatements that verify the caller is authorized to modify pause state. - Pause state operation detection: Identifies assignments of boolean-like values (0 or 1) to parameter-range variables as pause state modifications.
- Critical operation scanning: Flags
TransferLamportsandStoreAccountDataas operations that should respect pause state. - Gap analysis: Generates findings when (a) critical operations exist without any pause checks in transfer-like functions, (b) pause state is modified without authority verification, or (c) critical operations proceed without checking pause state.
- Context adjustment: Confidence is reduced for Anchor programs, admin functions, and read-only functions.
Limitations
False positives:
- Programs where pausing is handled by a separate guardian program via CPI.
- Anchor programs where pause constraints are enforced in account validation.
- Intentionally non-pausable programs (e.g., immutable DeFi primitives).
False negatives:
- Complex pause implementations where the state is stored in nested account structures.
- Programs that use program upgrade authority as an implicit pause mechanism.
Related Detectors
- Timelock Operations — missing delays for critical operations
- Admin Key Management — pause authority management
- Role-Based Access Control — role checks for pause authority