Instruction Verification Pattern
Detects incorrect ordering of validation checks and sensitive operations, where state modifications or CPIs occur before authorization.
Instruction Verification Pattern
Overview
Remediation Guide: How to Fix Instruction Verification Pattern
The instruction verification pattern detector identifies Solana programs that perform sensitive operations (state modifications, lamport transfers, cross-program invocations) before completing required validation checks (signer verification, owner verification, key comparison). This class of vulnerability was responsible for the Wormhole Bridge exploit in February 2022, where $320M was stolen due to an instruction ordering issue that allowed verification to be bypassed.
The detector uses control flow graph analysis with dataflow tracking to determine whether validation checks dominate sensitive operations across all execution paths. It identifies four categories: state modification before signer check, CPI before authorization, lamport transfer before signer check, and account data read without owner check.
Why This Is an Issue
When a program modifies state or transfers funds before checking authorization, an attacker may be able to reach the sensitive operation without providing valid credentials. Even if the validation check exists later in the function, the damage has already been done. In programs with multiple execution paths, validation may exist on one path but be absent on another, creating an exploitable gap.
How to Resolve
pub fn process_withdraw(
program_id: &Pubkey,
accounts: &[AccountInfo],
amount: u64,
) -> ProgramResult {
let authority = &accounts[0];
let vault = &accounts[1];
// 1. Validate FIRST
if !authority.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
if vault.owner != program_id {
return Err(ProgramError::IncorrectProgramId);
}
// 2. THEN perform sensitive operations
**vault.try_borrow_mut_lamports()? -= amount;
**authority.try_borrow_mut_lamports()? += amount;
Ok(())
}
Examples
Vulnerable Code
pub fn process_withdraw(
_program_id: &Pubkey,
accounts: &[AccountInfo],
amount: u64,
) -> ProgramResult {
let authority = &accounts[0];
let vault = &accounts[1];
// State modification BEFORE signer check
**vault.try_borrow_mut_lamports()? -= amount;
**authority.try_borrow_mut_lamports()? += amount;
// Validation happens too late
if !authority.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
Ok(())
}
Fixed Code
pub fn process_withdraw(
program_id: &Pubkey,
accounts: &[AccountInfo],
amount: u64,
) -> ProgramResult {
let authority = &accounts[0];
let vault = &accounts[1];
if !authority.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
if vault.owner != program_id {
return Err(ProgramError::IncorrectProgramId);
}
**vault.try_borrow_mut_lamports()? -= amount;
**authority.try_borrow_mut_lamports()? += amount;
Ok(())
}
Sample Sigvex Output
[HIGH] State Modification Before Signer Validation
Location: process_withdraw (block 0, stmt 2)
Description: StoreAccountData occurs before CheckSigner validation.
Signer authority may not have been verified before state change.
CWE: CWE-862 (Missing Authorization)
Detection Methodology
- Dataflow analysis: The detector performs forward dataflow analysis across the control flow graph, tracking which accounts have been validated (signer check, owner check, key check) at each program point.
- Sensitive operation identification: It identifies state modifications (
StoreAccountData), lamport transfers (TransferLamports), and cross-program invocations (InvokeCpi). - Validation ordering verification: For each sensitive operation, the detector checks whether the required validation has been performed on all paths from the function entry to that operation.
- PDA awareness: Programs that derive PDAs (program derived addresses) receive implicit authorization credit, as PDA derivation serves as a form of programmatic verification.
Limitations
False positives: Programs that validate authorization in a separate function called before the sensitive operation may be flagged if the analysis is intraprocedural. PDA-based authorization patterns reduce but do not eliminate false positives. False negatives: Validation checks stored in complex data structures or performed through indirect function calls may not be tracked.
Related Detectors
- Implicit Instruction Ordering — detects ordering dependencies not enforced on all CFG paths
- Instruction Fallback Handler — detects unsafe default instruction handlers