Uninitialized Account Remediation
How to fix uninitialized account access vulnerabilities.
Uninitialized Account Remediation
Overview
Detector Reference: Uninitialized Account
This guide explains how to prevent reading from uninitialized accounts, which can lead to type confusion and attacker-controlled state injection.
Recommended Fix
Always check that an account is initialized before reading its data:
let account = &accounts[0];
// Check 1: Account has data
require!(!account.data_is_empty(), ProgramError::UninitializedAccount);
// Check 2: Discriminator is valid
let data = account.data.borrow();
require!(data[0..8] == EXPECTED_DISCRIMINATOR, ProgramError::InvalidAccountData);
// Check 3: Owner is correct
require!(*account.owner == program_id, ProgramError::IncorrectProgramId);
// Now safe to deserialize
let state = MyState::try_from_slice(&data[8..])?;
For Anchor, use typed Account<'info, T> which validates discriminator and owner automatically.
Alternative Mitigations
- Use
data_is_empty(): a quick check that the account has been allocated. - Owner check: uninitialized accounts are owned by the system program. Check
account.owner == &program_idto confirm your program owns it. - Anchor typed accounts:
Account<'info, MyState>validates discriminator and owner at deserialization time.
Common Mistakes
- Checking only data length: a non-empty account may still be uninitialized if created but never written to by your program. Always check the discriminator.
- Using
UncheckedAccountin Anchor: bypasses all automatic validation. Add manual checks if usingUncheckedAccount. - Trusting
init_if_needed: the account may have been pre-created by an attacker. Validate owner and discriminator even withinit_if_needed.