Instruction Verification Pattern Remediation
How to fix incorrect ordering of validation checks and sensitive operations.
Instruction Verification Pattern Remediation
Overview
Related Detector: Instruction Verification Pattern
Instruction verification pattern violations occur when a program performs sensitive operations (state writes, transfers, CPIs) before completing all required validation checks. The fix is to structure every instruction handler with a clear validation-then-execute pattern: perform all signer, owner, and key checks before any state modification or fund transfer.
Recommended Fix
Before (Vulnerable)
pub fn process(accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
let user = &accounts[0];
let vault = &accounts[1];
// Writes state before checking authorization
let mut vault_data = vault.try_borrow_mut_data()?;
vault_data[0..8].copy_from_slice(&data[0..8]);
if !user.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
Ok(())
}
After (Fixed)
pub fn process(accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
let user = &accounts[0];
let vault = &accounts[1];
// All validation first
if !user.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
if vault.owner != &crate::ID {
return Err(ProgramError::IncorrectProgramId);
}
// State modification only after validation
let mut vault_data = vault.try_borrow_mut_data()?;
vault_data[0..8].copy_from_slice(&data[0..8]);
Ok(())
}
Alternative Mitigations
Use Anchor constraints to enforce validation before the handler body executes:
#[derive(Accounts)]
pub struct UpdateVault<'info> {
#[account(mut, has_one = authority)]
pub vault: Account<'info, Vault>,
pub authority: Signer<'info>,
// Anchor verifies signer + ownership before handler runs
}
Extract a validation function that returns a validated context struct. The handler only receives the struct, ensuring validation always precedes execution.
Common Mistakes
Performing validation in only one branch of a conditional. If a function branches on user input, both paths must validate before executing sensitive operations.
Checking the signer but not the owner. Both checks are typically required. A valid signer operating on an account owned by the wrong program can still cause damage.
Assuming CPI callees will validate. The calling program must validate its own preconditions before invoking another program. The callee validates its own constraints, not the caller’s.