Instruction Introspection
Detects unsafe instruction introspection with missing bounds or length validation when accessing transaction instructions via sysvar.
Instruction Introspection
Overview
Remediation Guide: How to Fix Instruction Introspection
The instruction introspection detector identifies unsafe use of the Instructions sysvar (sol_get_sysvar_instructions) to access other instructions within the current transaction. Programs use instruction introspection to verify that specific instructions precede or follow the current one (e.g., confirming a price oracle update occurred). Without proper bounds validation, accessing a non-existent instruction index causes a runtime panic.
This detector flags three categories of issues: missing bounds checks before loading an instruction by index, missing data length validation before deserializing instruction data, and use of suspicious or invalid constant indices (negative values or unusually high values).
Why This Is an Issue
An attacker controls the transaction structure and can craft transactions with fewer instructions than the program expects. If the program accesses instruction index 2 but the transaction only contains 2 instructions (indices 0 and 1), the runtime panics. This enables denial of service and can break authorization logic that depends on verifying the presence of specific preceding instructions.
How to Resolve
use solana_program::sysvar::instructions::{
load_current_index_checked, load_instruction_at_checked,
};
pub fn verify_preceding_instruction(
instruction_sysvar: &AccountInfo,
) -> ProgramResult {
let current_index = load_current_index_checked(instruction_sysvar)?;
// Validate bounds before access
if current_index == 0 {
return Err(ProgramError::InvalidArgument); // No preceding instruction
}
let prev_ix = load_instruction_at_checked(
(current_index - 1) as usize,
instruction_sysvar,
)?;
// Validate data length before deserialization
if prev_ix.data.len() < 8 {
return Err(ProgramError::InvalidInstructionData);
}
let value = u64::from_le_bytes(prev_ix.data[0..8].try_into().unwrap());
Ok(())
}
Examples
Vulnerable Code
pub fn check_oracle(instruction_sysvar: &AccountInfo) -> ProgramResult {
// No bounds check -- panics if instruction index 1 doesn't exist
let oracle_ix = load_instruction_at_checked(1, instruction_sysvar)?;
// No length check -- panics if data is too short
let price = u64::from_le_bytes(oracle_ix.data[0..8].try_into().unwrap());
Ok(())
}
Fixed Code
pub fn check_oracle(instruction_sysvar: &AccountInfo) -> ProgramResult {
let count = load_current_index_checked(instruction_sysvar)? as usize + 1;
if count < 2 {
return Err(ProgramError::InvalidArgument);
}
let oracle_ix = load_instruction_at_checked(1, instruction_sysvar)?;
if oracle_ix.data.len() < 8 {
return Err(ProgramError::InvalidInstructionData);
}
let price = u64::from_le_bytes(oracle_ix.data[0..8].try_into().unwrap());
Ok(())
}
Sample Sigvex Output
[HIGH] Instruction Introspection Without Bounds Check
Location: check_oracle (block 0, stmt 0)
Description: Instruction introspection call uses index without bounds
validation. An attacker can craft transactions with fewer instructions.
CWE: CWE-129 (Improper Validation of Array Index)
Detection Methodology
- Syscall identification: The detector identifies calls to instruction introspection syscalls (
sol_get_sysvar_instructions,load_instruction_at,get_instruction). - Bounds validation tracking: For each introspection call, it checks whether the index argument was previously validated via a conditional comparison, or whether instruction count was queried before the access.
- Length validation tracking: After the introspection result is obtained, the detector checks whether the instruction data length is validated before deserialization.
- Constant index analysis: Negative indices trigger critical findings. Indices of 10 or higher trigger medium-severity findings as suspicious.
Limitations
False positives: Programs that use load_instruction_at_checked (the safe variant) may still be flagged if the detector does not distinguish checked from unchecked variants at the bytecode level. False negatives: Bounds checks in helper functions called before the introspection may not be detected without interprocedural analysis.
Related Detectors
- Instruction Index Validation — detects hardcoded instruction index comparisons
- Instruction Verification Pattern — detects incorrect ordering of validation and operations