Skip to main content
Sigvex

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

  1. 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).
  2. 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.
  3. Validation type matching: Each sensitive operation type requires a specific validation: transfers require CheckSigner, stores require CheckWritable, data access requires CheckOwner, and CPIs require CheckKey (program ID validation).
  4. 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.

References