Skip to main content
Sigvex

Instruction Index Validation

Detects hardcoded instruction index comparisons that can be bypassed by attackers who control transaction structure.

Instruction Index Validation

Overview

Remediation Guide: How to Fix Instruction Index Validation

The instruction index validation detector identifies Solana programs that compare the current instruction index against hardcoded constants (e.g., index == 0 to ensure “this instruction is first in the transaction”). Attackers can bypass these checks by prepending harmless instructions to the transaction, shifting the target instruction to a different index.

This detector flags two patterns: direct comparisons of the current instruction index against constants, and loading instructions at hardcoded indices without first validating the total instruction count in the transaction.

Why This Is an Issue

Solana transactions can contain an arbitrary number of instructions, and the caller fully controls the transaction layout. A check like require!(current_index == 0) – intended to ensure the instruction runs first – is trivially bypassed by prepending a no-op instruction from any program. This makes the target instruction appear at index 1 instead of 0, bypassing the check entirely. Programs that rely on instruction ordering for security (e.g., flash loan repayment verification) are particularly vulnerable.

How to Resolve

use solana_program::sysvar::instructions::{
    get_instruction_relative, load_current_index_checked,
    load_instruction_at_checked,
};

pub fn verify_instruction_context(
    ix_sysvar: &AccountInfo,
    expected_program: &Pubkey,
) -> ProgramResult {
    // Validate total instruction count instead of hardcoded index
    let current = load_current_index_checked(ix_sysvar)? as usize;

    // Verify the preceding instruction is from the expected program
    if current > 0 {
        let prev = load_instruction_at_checked(current - 1, ix_sysvar)?;
        if prev.program_id != *expected_program {
            return Err(ProgramError::InvalidArgument);
        }
    }
    Ok(())
}

Examples

Vulnerable Code

pub fn enforce_first(ix_sysvar: &AccountInfo) -> ProgramResult {
    let current = load_current_index_checked(ix_sysvar)?;
    // Attacker prepends a no-op instruction, making this index 1
    if current != 0 {
        return Err(ProgramError::InvalidArgument);
    }
    Ok(())
}

Fixed Code

pub fn enforce_context(
    ix_sysvar: &AccountInfo,
    expected_count: u16,
) -> ProgramResult {
    let current = load_current_index_checked(ix_sysvar)?;
    // Validate total instruction count to prevent prepending attacks
    let total = (current + 1) as u16; // Approximate; use get_instruction_count if available
    if total != expected_count {
        return Err(ProgramError::InvalidArgument);
    }
    Ok(())
}

Sample Sigvex Output

[HIGH] Hardcoded Instruction Index Comparison
  Location: enforce_first (block 0, stmt 1)
  Description: Instruction index is compared against hardcoded constant
    (index == 0). Attackers can manipulate transaction structure by
    prepending instructions.
  CWE: CWE-807 (Reliance on Untrusted Inputs in a Security Decision)

Detection Methodology

  1. Index variable identification: The detector identifies variables assigned from syscalls that return the current instruction index (load_current_index_checked, get_instruction_index).
  2. Hardcoded comparison detection: It searches for conditional branches that compare identified index variables against constant values.
  3. Count validation awareness: If the program also queries the instruction count, confidence is reduced because the count check partially mitigates the risk.
  4. Hardcoded access detection: Direct calls to load_instruction_at with constant index arguments are flagged when no instruction count validation exists.

Limitations

False positives: Programs that validate instruction count alongside index checks may still be flagged, though with reduced confidence. Legitimate use of fixed transaction layouts (e.g., atomic swap protocols with exactly 2 instructions) may trigger findings. False negatives: Index values computed from expressions rather than direct constants may not be detected.

References