Account Realloc Corruption Remediation
How to fix account reallocation data corruption vulnerabilities.
Account Realloc Corruption Remediation
Overview
Detector Reference: Account Realloc Corruption
This guide explains how to prevent data corruption during account reallocation by validating sizes and bounds.
Recommended Fix
Validate size before reallocation and bounds before data access:
// 1. Validate new size
require!(new_size >= MIN_STRUCT_SIZE, ErrorCode::InvalidSize);
require!(new_size <= MAX_ALLOWED_SIZE, ErrorCode::InvalidSize);
// 2. If shrinking, ensure no data loss
if new_size < account.data_len() {
let data = account.data.borrow();
require!(all_trailing_fields_empty(&data[new_size..]), ErrorCode::WouldTruncateData);
}
// 3. Realloc with zero-fill
account.realloc(new_size, true)?;
// 4. Bounds check before access
let data = account.data.borrow();
require!(offset + std::mem::size_of::<u64>() <= data.len(), ErrorCode::OutOfBounds);
let value = u64::from_le_bytes(data[offset..offset + 8].try_into()?);
Alternative Mitigations
- Reject shrinking entirely: if data migration is not needed, only allow growing.
- Use Anchor realloc:
#[account(mut, realloc = size, realloc::payer = payer, realloc::zero = true)]handles safety automatically. - Minimum size constant: define
const MIN_ACCOUNT_SIZE: usize = std::mem::size_of::<MyStruct>() + 8;and enforce it.
Common Mistakes
- Allowing zero-size reallocation: this destroys all data. Use a separate close instruction instead of
realloc(0). - Not re-validating field offsets: after reallocation, all field offsets must be checked against the new size.
- Skipping zero-fill on grow: newly allocated memory may contain stale data. Use
realloc(size, true).