CPI Data Tampering
Detects CPI calls with potentially tampered instruction data from untrusted sources.
CPI Data Tampering
Overview
Remediation Guide: How to Fix CPI Data Tampering
The CPI data tampering detector identifies cross-program invocations where instruction data or account lists come from untrusted sources (user input, account data, memory loads) without validation. Attackers can craft malicious instruction data to call unintended program functions, bypass instruction-level access controls, or manipulate parameters to privileged operations.
Why This Is an Issue
CPI instruction data determines which function the called program executes and with what parameters. If this data comes from user-controlled input without validation, an attacker can change the instruction discriminator to call admin functions, alter amounts in transfer operations, or modify any parameter the called program processes. This effectively gives attackers arbitrary control over the called program’s execution.
CWE mapping: CWE-20 (Improper Input Validation).
How to Resolve
Native Solana
pub fn safe_cpi(accounts: &[AccountInfo], user_amount: u64) -> ProgramResult {
// Build instruction data from validated components, not raw user input
let ix_data = spl_token::instruction::TokenInstruction::Transfer { amount: user_amount }.pack();
// Validate amount bounds
if user_amount == 0 || user_amount > MAX_TRANSFER {
return Err(ProgramError::InvalidArgument);
}
let ix = Instruction { program_id: spl_token::id(), accounts: /* ... */, data: ix_data };
invoke(&ix, accounts)?;
Ok(())
}
Anchor
// Anchor CpiContext builds typed instruction data, preventing raw tampering
pub fn transfer(ctx: Context<Transfer>, amount: u64) -> Result<()> {
require!(amount > 0 && amount <= MAX_TRANSFER, ErrorCode::InvalidAmount);
let cpi_ctx = CpiContext::new(
ctx.accounts.token_program.to_account_info(),
token::Transfer { /* typed accounts */ },
);
token::transfer(cpi_ctx, amount)?;
Ok(())
}
Examples
Vulnerable Code
pub fn proxy_call(accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
// Raw user data passed directly to CPI -- attacker controls instruction type
let ix = Instruction {
program_id: *accounts[3].key,
accounts: /* ... */,
data: data.to_vec(), // VULNERABLE: raw user input as instruction data
};
invoke(&ix, accounts)?;
Ok(())
}
Fixed Code
pub fn proxy_call(accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
// Validate instruction discriminator
if data.len() < 8 { return Err(ProgramError::InvalidInstructionData); }
let discriminator = &data[0..8];
if discriminator != ALLOWED_INSTRUCTION_DISCRIMINATOR {
return Err(ProgramError::InvalidInstructionData);
}
let ix = Instruction { program_id: *accounts[3].key, accounts: /* ... */, data: data.to_vec() };
invoke(&ix, accounts)?;
Ok(())
}
Sample Sigvex Output
{
"detector_id": "cpi-data-tampering",
"severity": "high",
"confidence": 0.55,
"description": "CPI call uses instruction data from an untrusted source without validation. Attackers can craft malicious instruction data to call unintended functions.",
"location": { "function": "proxy_call", "block": 0, "stmt": 1 }
}
Detection Methodology
- Taint analysis: Identifies instruction data and account list expressions that originate from parameters, memory loads, or variables (untrusted sources).
- Validation detection: Checks for existing data validation patterns including discriminator comparisons and bounds checks on loaded data.
- Dynamic construction detection: Flags CPI calls with instruction data built via binary operations or memory manipulation.
- Context adjustment: Confidence is significantly reduced for Anchor programs, which use typed instruction builders.
Limitations
- The detector conservatively treats all variable-sourced data as untrusted, which may flag validated data that flows through complex paths.
- Instruction builders from external crates that construct safe data are not recognized.
- Validation performed in separate functions is not tracked across call boundaries.
Related Detectors
- CPI Account List Mismatch — validates CPI account lists
- Input Validation — detects general input validation issues