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.
Recommended Fix
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
- Anchor typed accounts:
Account<'info, T>validates discriminator and owner and deserializes safely. - Manual field extraction: for simple structs, extract fields with explicit bounds checks instead of full Borsh deserialization.
- Schema versioning: include a version field and validate it before deserializing, supporting graceful migration.
Common Mistakes
- Using
unwrap()ontry_from_slice: this panics on malformed data. Always use?ormap_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.