Skip to main content
Sigvex

Account Index Bounds

Detects account array access without bounds validation, a vulnerability that appears in 30% of recent Solana audit findings.

Account Index Bounds

Overview

Remediation Guide: How to Fix Account Index Bounds

The account index bounds detector identifies Solana programs that access account arrays using an index without first validating that the index is within the array bounds. This is one of the most frequently reported vulnerabilities in Solana program audits, appearing in approximately 30% of recent findings.

In native Solana programs, accounts are passed as a slice &[AccountInfo] and accessed by index. If the program reads a user-supplied index from instruction data and uses it directly (e.g., accounts[user_index]), an out-of-bounds index causes a runtime panic. The detector tracks variable indices, constant indices, and complex expressions used to access account properties (data, lamports, owner, key).

Why This Is an Issue

An attacker can trigger a runtime panic by providing an account index that exceeds the array length. This enables denial of service (crashing the program), and in more subtle cases, can cause account confusion where the wrong account is accessed due to an off-by-one error. If the index is derived from instruction data without bounds checking, the attacker has full control over which account is accessed.

How to Resolve

pub fn process(
    _program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    let target_idx = instruction_data[0] as usize;

    // Validate bounds before access
    if target_idx >= accounts.len() {
        return Err(ProgramError::NotEnoughAccountKeys);
    }
    let target = &accounts[target_idx];
    Ok(())
}

Examples

Vulnerable Code

pub fn process(
    _program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    let idx = instruction_data[0] as usize;
    // No bounds check -- panics if idx >= accounts.len()
    let account = &accounts[idx];
    let owner = account.owner;
    Ok(())
}

Fixed Code

pub fn process(
    _program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    let idx = instruction_data[0] as usize;
    let account = accounts.get(idx)
        .ok_or(ProgramError::NotEnoughAccountKeys)?;
    let owner = account.owner;
    Ok(())
}

Sample Sigvex Output

[HIGH] Account Array Access Without Bounds Validation
  Location: process (block 0, stmt 1)
  Description: The function accesses account[v5] via AccountData without
    verifying that the index is within bounds.
  CWE: CWE-129 (Improper Validation of Array Index)

Detection Methodology

  1. Index tracking: The detector tracks all variables used as indices when accessing account properties (data, lamports, owner, key).
  2. Bounds check identification: It searches for conditional comparisons of the form index < max or max > index that validate the index variable before access.
  3. Constant index heuristic: Small constant indices (0-5) are considered likely safe for typical programs. Constant indices above 5 are flagged as potentially unsafe.
  4. Access type coverage: The detector covers all account property access types: AccountData, AccountLamports, AccountOwner, and AccountKey.

Limitations

False positives: Programs with small, fixed account layouts may use constant indices that are always valid but get flagged if the index exceeds the small-constant threshold. Anchor programs using #[derive(Accounts)] receive reduced confidence because Anchor validates account count automatically. False negatives: Bounds checks performed in separate functions or through complex control flow may not be detected.

References