Skip to main content
Sigvex

Unchecked Deserialization

Detects unchecked deserialization of account data that may panic on malformed input.

Unchecked Deserialization

Overview

Remediation Guide: How to Fix Unchecked Deserialization

The unchecked deserialization detector identifies Solana programs that deserialize account data without proper error handling. When deserialization results are unwrapped, cast without validation, or used without discriminator checks, malformed account data can cause panics, denial of service, or processing of invalid state.

Sigvex tracks account data loads, subsequent cast/deserialization operations, and verifies whether error-handling branches protect the deserialized values before use.

Why This Is an Issue

  • Program panics: unwrap() or expect() on malformed data crashes the program, wasting compute budget.
  • Denial of service: an attacker can repeatedly submit transactions with malformed accounts to crash the program.
  • Invalid state processing: if deserialization fails silently (e.g., reading garbage bytes as valid data), the program may execute incorrect logic.

CWE mapping: CWE-690 (Unchecked Return Value), CWE-20 (Improper Input Validation).

How to Resolve

Native Solana

pub fn process(account: &AccountInfo) -> ProgramResult {
    let data = account.data.borrow();

    // Check data length
    if data.len() < MyStruct::LEN {
        return Err(ProgramError::InvalidAccountData);
    }

    // Check discriminator
    if data[0..8] != MY_DISCRIMINATOR {
        return Err(ProgramError::InvalidAccountData);
    }

    // Safe deserialization with error handling
    let state = MyStruct::try_from_slice(&data[8..])
        .map_err(|_| ProgramError::InvalidAccountData)?;

    process_state(state)
}

Anchor

// Anchor validates discriminator and deserializes automatically
#[derive(Accounts)]
pub struct Process<'info> {
    pub state: Account<'info, MyState>,
}

Examples

Vulnerable

let data = account.data.borrow();
let state = MyStruct::try_from_slice(&data).unwrap(); // Panics on malformed data!

Fixed

let data = account.data.borrow();
let state = MyStruct::try_from_slice(&data)
    .map_err(|_| ProgramError::InvalidAccountData)?;

JSON Finding

{
  "detector": "unchecked-deserialization",
  "severity": "High",
  "confidence": 0.75,
  "title": "Unchecked Deserialization Result",
  "description": "Deserialized account data is used without error handling.",
  "cwe": [690]
}

Detection Methodology

The detector identifies account data variables, tracks deserialization-like operations (casts, complex expressions on account data), and checks whether error-handling branches protect the result before use. It also reports functions that process multiple account data fields without any conditional branches, suggesting absent validation.

Limitations

  • Cast operations are used as a proxy for deserialization since exact Borsh calls are not distinguishable in decompiled bytecode.
  • The detector may produce false positives for simple field extractions that do not require error handling.
  • Programs with discriminator validation receive reduced confidence since type identity is already verified.

References