Skip to main content
Sigvex

Insufficient Data Length Check Remediation

How to fix account data access without sufficient length validation.

Insufficient Data Length Check Remediation

Overview

Related Detector: Insufficient Data Length Check

Programs that read or write account data without verifying the buffer is large enough can cause out-of-bounds access or runtime panics. The fix is to check account.data.len() against the required size before any read or write operation.

Before (Vulnerable)

pub fn update_config(accounts: &[AccountInfo], new_value: u64) -> ProgramResult {
    let config = &accounts[0];
    let mut data = config.try_borrow_mut_data()?;
    // Writes 8 bytes at offset 32 without checking data length
    data[32..40].copy_from_slice(&new_value.to_le_bytes());
    Ok(())
}

After (Fixed)

pub fn update_config(accounts: &[AccountInfo], new_value: u64) -> ProgramResult {
    let config = &accounts[0];
    let mut data = config.try_borrow_mut_data()?;

    if data.len() < 40 {
        return Err(ProgramError::InvalidAccountData);
    }
    data[32..40].copy_from_slice(&new_value.to_le_bytes());
    Ok(())
}

Alternative Mitigations

Use the account-validation framework’s typed accounts to validate data length automatically during deserialization:

#[account]
pub struct Config {
    pub authority: Pubkey,  // 32 bytes
    pub value: u64,         // 8 bytes
    // Anchor validates data.len() >= 8 + 32 + 8 during deserialization
}

Define constants for all expected account sizes and validate at the top of each handler:

const CONFIG_SIZE: usize = 8 + 32 + 8; // discriminator + pubkey + u64

pub fn process(accounts: &[AccountInfo]) -> ProgramResult {
    let config = &accounts[0];
    require!(config.data_len() >= CONFIG_SIZE, InvalidAccountData);
    // Safe to access up to CONFIG_SIZE bytes
    Ok(())
}

Common Mistakes

Checking the length for reads but not writes. Writes to undersized buffers are more dangerous than reads because they corrupt adjacent memory.

Using the wrong size constant. When account layouts change (e.g., adding a new field), the length constant must be updated. Consider using std::mem::size_of::<T>() for Borsh-serialized structs.

Checking length in the initializer but not in update handlers. Account data can be reallocated to a smaller size. Every handler that accesses account data should validate the length independently.

References