Skip to main content
Sigvex

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.

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

  1. Reject shrinking entirely: if data migration is not needed, only allow growing.
  2. Use Anchor realloc: #[account(mut, realloc = size, realloc::payer = payer, realloc::zero = true)] handles safety automatically.
  3. 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).

References