Instruction Introspection Remediation
How to fix unsafe instruction introspection with missing bounds or length validation.
Instruction Introspection Remediation
Overview
Related Detector: Instruction Introspection
Instruction introspection via the Instructions sysvar allows programs to inspect other instructions in the same transaction. Accessing an instruction by index without verifying the transaction contains enough instructions causes a runtime panic. The fix requires validating the instruction count before index-based access and validating data length before deserialization.
Recommended Fix
Before (Vulnerable)
pub fn verify_flash_loan(ix_sysvar: &AccountInfo) -> ProgramResult {
let repay_ix = load_instruction_at_checked(2, ix_sysvar)?;
let amount = u64::from_le_bytes(repay_ix.data[0..8].try_into().unwrap());
Ok(())
}
After (Fixed)
pub fn verify_flash_loan(ix_sysvar: &AccountInfo) -> ProgramResult {
// 1. Validate instruction count
let current = load_current_index_checked(ix_sysvar)? as usize;
if current + 1 < 3 {
return Err(ProgramError::InvalidArgument);
}
// 2. Load instruction with validated index
let repay_ix = load_instruction_at_checked(2, ix_sysvar)?;
// 3. Validate data length before deserialization
if repay_ix.data.len() < 8 {
return Err(ProgramError::InvalidInstructionData);
}
let amount = u64::from_le_bytes(repay_ix.data[0..8].try_into().unwrap());
Ok(())
}
Alternative Mitigations
Use CPI instead of introspection when possible. Cross-program invocations are safer because the callee validates its own inputs, and you do not need to reason about transaction-level instruction ordering.
Validate the total instruction count at the program entry point rather than at each introspection site. This centralizes the check and ensures all downstream introspection calls are safe.
Common Mistakes
Assuming a fixed transaction layout. Attackers control which instructions appear in a transaction and in what order. Never assume instruction index N always contains a specific program’s instruction.
Checking only the index but not the data length. Even if the instruction exists, its data payload may be shorter than expected.
Using relative offsets without accounting for prepended instructions. If you access current_index - 1, verify that current_index > 0 first.