Skip to main content
Sigvex

Unsafe Deserialization

Detects potentially unsafe account data access patterns without bounds checking.

Unsafe Deserialization

Overview

Remediation Guide: How to Fix Unsafe Deserialization

The unsafe deserialization detector identifies Solana programs that access account data without bounds checking. When account data is read or written using variable offsets without validating that the offset is within the account’s data length, the program may panic from out-of-bounds access or write beyond the allocation boundary.

Sigvex flags variable-offset reads and writes to account data, as well as large fixed-size writes that may exceed the account’s allocated space.

Why This Is an Issue

  • Program panics: out-of-bounds reads cause the BPF runtime to abort the program, wasting compute budget and failing the transaction.
  • Out-of-bounds writes: writing beyond the account boundary corrupts adjacent memory, potentially modifying other program state.
  • Denial of service: an attacker can craft instruction data with malicious offsets to reliably crash the program.

CWE mapping: CWE-125 (Out-of-bounds Read), CWE-787 (Out-of-bounds Write).

How to Resolve

Native Solana

pub fn read_field(account: &AccountInfo, offset: usize, size: usize) -> ProgramResult {
    let data = account.data.borrow();

    // Validate bounds before access
    require!(offset + size <= data.len(), ProgramError::InvalidAccountData);

    let value = &data[offset..offset + size];
    // Process value...
    Ok(())
}

Anchor

// Anchor handles deserialization via Borsh with automatic bounds checking
#[account]
pub struct MyState {
    pub authority: Pubkey,
    pub balance: u64,
}

Examples

Vulnerable

let data = account.data.borrow();
let offset = instruction_data[0] as usize; // User-controlled offset
let value = u64::from_le_bytes(data[offset..offset + 8].try_into()?); // No bounds check!

Fixed

let data = account.data.borrow();
let offset = instruction_data[0] as usize;
require!(offset + 8 <= data.len(), InvalidAccountData);
let value = u64::from_le_bytes(data[offset..offset + 8].try_into()?);

JSON Finding

{
  "detector": "unsafe-deserialization",
  "severity": "Medium",
  "confidence": 0.65,
  "title": "Variable Offset Account Data Read",
  "description": "Account data is read with a variable offset without bounds checking.",
  "cwe": [125]
}

Detection Methodology

The detector scans AccountData expressions and StoreAccountData statements for variable offsets (non-constant expressions). It also flags large fixed-size writes (8 bytes) that could exceed small accounts. The analysis recurses into nested expressions to catch indirect account data access patterns.

Limitations

  • Constant offsets are not flagged even if they exceed typical account sizes.
  • The detector cannot verify whether bounds checks exist in helper functions.
  • Anchor programs receive reduced confidence since Borsh deserialization includes built-in bounds checking.

References