Skip to main content
Sigvex

Unchecked Deserialization Remediation

How to fix unchecked deserialization vulnerabilities.

Unchecked Deserialization Remediation

Overview

Detector Reference: Unchecked Deserialization

This guide explains how to fix deserialization patterns that lack error handling, preventing panics and denial of service from malformed account data.

Use try_from_slice with the ? operator instead of unwrap():

// 1. Validate length
require!(account.data.len() >= MyStruct::LEN, InvalidAccountData);

// 2. Validate discriminator
let data = account.data.borrow();
require!(data[0..8] == MY_DISCRIMINATOR, InvalidAccountData);

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

Alternative Mitigations

  1. Anchor typed accounts: Account<'info, T> validates discriminator and owner and deserializes safely.
  2. Manual field extraction: for simple structs, extract fields with explicit bounds checks instead of full Borsh deserialization.
  3. Schema versioning: include a version field and validate it before deserializing, supporting graceful migration.

Common Mistakes

  • Using unwrap() on try_from_slice: this panics on malformed data. Always use ? or map_err.
  • Skipping discriminator validation: without discriminator checks, the program may deserialize a completely different account type.
  • Ignoring data length: Borsh deserialization can succeed on truncated data for some types, producing incorrect values.

References