Implicit Instruction Ordering
Detects implicit instruction ordering dependencies not enforced on all control flow paths, enabling authorization bypass through path-selective attacks.
Implicit Instruction Ordering
Overview
Remediation Guide: How to Fix Implicit Instruction Ordering
The implicit instruction ordering detector identifies Solana programs where sensitive operations are reachable through control flow paths that lack required validation. Unlike simple ordering checks that verify validation occurs before an operation on a single path, this detector uses CFG traversal to enumerate all paths from the function entry to each sensitive operation and verifies that every path includes the required validation.
The detector covers four categories: transfers without universal signer validation, stores without universal writable validation, account data access without universal owner validation, and CPIs without universal program ID validation. A finding is generated when even one path to a sensitive operation lacks the required check.
Why This Is an Issue
When a program branches on user-controlled input and only one branch validates authorization, an attacker can choose the unvalidated branch to reach the sensitive operation without providing credentials. This is a path-selective authorization bypass: the validation exists in the program but is not enforced on all paths. The impact ranges from unauthorized fund transfers (high severity) to arbitrary cross-program invocations (critical severity).
How to Resolve
pub fn process(accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
let authority = &accounts[0];
let vault = &accounts[1];
// Validate BEFORE branching -- ensures all paths are covered
if !authority.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
if vault.owner != &crate::ID {
return Err(ProgramError::IncorrectProgramId);
}
// Now branch on user input -- both paths are already validated
if data[0] == 1 {
transfer(vault, authority, 1000)?;
} else {
transfer(vault, authority, 500)?;
}
Ok(())
}
Examples
Vulnerable Code
pub fn process(accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
let authority = &accounts[0];
let vault = &accounts[1];
if data[0] == 1 {
// Path 1: validates signer
if !authority.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
transfer(vault, authority, 1000)?;
} else {
// Path 2: NO signer check -- attacker uses this path
transfer(vault, authority, 1000)?;
}
Ok(())
}
Fixed Code
pub fn process(accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
let authority = &accounts[0];
let vault = &accounts[1];
// Signer check before any branching
if !authority.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
if data[0] == 1 {
transfer(vault, authority, 1000)?;
} else {
transfer(vault, authority, 500)?;
}
Ok(())
}
Sample Sigvex Output
[HIGH] Transfer without universal signer validation
Location: process (block 2, stmt 0)
Description: Transfer at block 2 stmt 0 is reachable via 1 path(s)
without CheckSigner validation for v1.
CWE: CWE-862 (Missing Authorization)
Detection Methodology
- Path enumeration: The detector uses BFS from the entry block to each block containing a sensitive operation, enumerating all possible execution paths (capped at 64 paths to prevent exponential blowup).
- Per-path validation check: For each path, the detector checks whether the required validation statement exists in any block along the path before the sensitive operation.
- Validation type matching: Each sensitive operation type requires a specific validation: transfers require
CheckSigner, stores requireCheckWritable, data access requiresCheckOwner, and CPIs requireCheckKey(program ID validation). - Variable tracking: The detector matches the account variable in the validation statement against the account variable in the sensitive operation to ensure the correct account is validated.
Limitations
False positives: Programs that validate authorization through indirect means (e.g., PDA derivation, CPI return values) may be flagged because the detector only recognizes explicit validation statements. The path enumeration cap of 64 may miss some paths in highly complex CFGs. False negatives: Validation performed through helper function calls or stored in data structures may not be detected.
Related Detectors
- Instruction Verification Pattern — detects incorrect ordering of validation and operations using dataflow analysis
- Instruction Index Validation — detects hardcoded instruction index comparisons