Skip to main content
Sigvex

Missing Empty Account Check Remediation

How to fix missing empty account validation before initialization.

Missing Empty Account Check Remediation

Overview

Detector Reference: Missing Empty Account Check

This guide explains how to fix initialization functions that write to accounts without first verifying the account is empty. Without this check, attackers can re-initialize accounts to overwrite authority keys, reset balances, or corrupt state.

Before writing initialization data, verify the account’s discriminator bytes are all zeros:

pub fn initialize(accounts: &[AccountInfo], authority: Pubkey) -> ProgramResult {
    let account = &accounts[0];
    let data = account.data.borrow();

    // Verify account is truly empty
    if data.len() >= 8 && data[0..8] != [0u8; 8] {
        return Err(ProgramError::AccountAlreadyInitialized);
    }
    drop(data);

    let mut data = account.data.borrow_mut();
    data[0..8].copy_from_slice(MY_DISCRIMINATOR);
    data[8..40].copy_from_slice(authority.as_ref());
    Ok(())
}

For an account-validation framework programs, use the init constraint which atomically creates and initializes accounts:

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(init, payer = user, space = 8 + 32)]
    pub state: Account<'info, MyState>,
    #[account(mut)]
    pub user: Signer<'info>,
    pub system_program: Program<'info, System>,
}

Alternative Mitigations

  1. Check data length: if the account’s data length is zero, it has not been allocated and is safe to initialize.
  2. Check owner: verify the account is still owned by the system program (indicating it has not been claimed by your program).
  3. Use data_is_empty(): Solana’s AccountInfo::data_is_empty() returns true for zero-length accounts.
if !account.data_is_empty() {
    return Err(ProgramError::AccountAlreadyInitialized);
}

Common Mistakes

  • Checking only the first byte: a single-byte check can miss partially initialized accounts. Always check the full 8-byte discriminator.
  • Using init_if_needed without ownership validation: the account-validation framework’s init_if_needed skips initialization for existing accounts but does not validate who created them. Pair it with an owner constraint.
  • Placing the check after the write: the empty check must execute before any write to the account. Placing it after is ineffective.

References