Unsafe Deserialization Remediation
How to fix unsafe account data access patterns.
Unsafe Deserialization Remediation
Overview
Detector Reference: Unsafe Deserialization
This guide explains how to fix unsafe account data access patterns that lack bounds checking.
Recommended Fix
Always validate that offsets and sizes are within bounds before accessing account data:
let data = account.data.borrow();
let offset = compute_offset(instruction_data)?;
let size = std::mem::size_of::<u64>();
// Bounds check
if offset.checked_add(size).map_or(true, |end| end > data.len()) {
return Err(ProgramError::InvalidAccountData);
}
let value = u64::from_le_bytes(data[offset..offset + size].try_into()?);
Alternative Mitigations
- Use Borsh deserialization:
MyStruct::try_from_slice(&data)handles bounds checking internally. - Fixed offsets: use compile-time constants for field offsets and validate account data length once at instruction entry.
- Anchor typed accounts:
Account<'info, MyState>deserializes with built-in bounds validation.
Common Mistakes
- Integer overflow in offset calculation:
offset + sizecan overflow. Usechecked_add()for safety. - Trusting instruction data for offsets: user-supplied offsets must always be validated against account length.
- Checking length once for multiple reads: if you perform multiple reads at different offsets, validate each one or validate the maximum required length upfront.