Account Realloc Corruption
Detects account reallocation data corruption vulnerabilities.
Account Realloc Corruption
Overview
Remediation Guide: How to Fix Account Realloc Corruption
The account realloc corruption detector identifies dangerous reallocation patterns where accounts are resized without proper size validation, where data is accessed after reallocation without bounds checking, or where accounts are reallocated to zero or minimal sizes. These patterns can cause data truncation, out-of-bounds reads, memory corruption, and information leakage from adjacent accounts.
Sigvex detects realloc operations (via CPI discriminators and syscall names) and analyzes the surrounding statements for size validation, bounds checks, and dangerous size arguments.
Why This Is an Issue
- Data truncation: reallocating to a smaller size destroys structured data beyond the new boundary without warning.
- Out-of-bounds reads: accessing fields at previous offsets after shrinking reads beyond the new allocation into adjacent memory.
- Zero-size corruption: reallocating to zero bytes destroys all data and causes subsequent deserialization to read garbage.
- Information leakage: reading beyond the new boundary may expose sensitive data from other accounts that previously occupied that memory.
CWE mapping: CWE-789 (Memory Allocation with Excessive Size Value), CWE-125 (Out-of-bounds Read), CWE-770 (Allocation Without Limits).
How to Resolve
Native Solana
// Validate new size before reallocation
let current_size = account.data_len();
require!(new_size >= MIN_ACCOUNT_SIZE, InvalidSize);
require!(new_size <= MAX_ACCOUNT_SIZE, InvalidSize);
// If shrinking, validate no data loss
if new_size < current_size {
require!(can_safely_shrink(account, new_size)?, DataLoss);
}
// Realloc
account.realloc(new_size, true)?;
// Re-validate bounds before access
require!(offset + field_size <= new_size, OutOfBounds);
let value = u64::from_le_bytes(data[offset..offset + 8].try_into()?);
Anchor
#[account(mut, realloc = new_size, realloc::payer = payer, realloc::zero = true)]
pub my_account: Account<'info, MyAccount>,
Examples
Vulnerable
// Realloc without size validation — attacker could request size 0
account.realloc(user_requested_size, false)?;
// Access data at old offset — may be out of bounds
let value = u64::from_le_bytes(data[64..72].try_into()?);
Fixed
require!(user_requested_size >= MIN_SIZE, InvalidSize);
account.realloc(user_requested_size, true)?;
require!(72 <= account.data_len(), OutOfBounds);
let data = account.data.borrow();
let value = u64::from_le_bytes(data[64..72].try_into()?);
JSON Finding
{
"detector": "account-realloc-corruption",
"severity": "Critical",
"confidence": 0.78,
"title": "Data Access After Reallocation Without Bounds Check",
"description": "Account data accessed after reallocation without bounds validation.",
"cwe": [125]
}
Detection Methodology
The detector identifies realloc operations via CPI instruction discriminators and syscall name matching. For each realloc, it checks: (1) whether size validation (comparison operations) precedes the realloc, (2) whether data access follows without bounds checks within 5 statements, and (3) whether the size argument is zero or dangerously small (less than 8 bytes).
Limitations
- Realloc identification uses heuristic matching of syscall names and CPI discriminators, which may miss custom realloc wrappers.
- The 5-statement window for bounds checks may miss checks that occur later.
- Anchor’s realloc constraint provides full safety guarantees but is not distinguishable in decompiled bytecode.
Related Detectors
- Account Reallocation - detects general reallocation safety issues.
- Rent Exempt Reallocation - detects realloc without rent adjustment.