Skip to main content
Sigvex

Anchor Constraint TOCTOU

Detects Time-of-Check-Time-of-Use vulnerabilities in Anchor constraint validation.

Anchor Constraint TOCTOU

Overview

The Anchor constraint TOCTOU detector identifies Time-of-Check-Time-of-Use vulnerabilities where account constraints read account data before a CPI call that may modify that same account. If the CPI changes the account data, the original constraint check becomes invalid, creating a race condition.

For remediation guidance, see Anchor Constraint TOCTOU Remediation.

Why This Is an Issue

In Anchor programs, account constraints are checked sequentially. If a constraint reads an account’s balance or state, and a subsequent CPI modifies that same account, the original check no longer reflects reality. An attacker can craft CPI calls that invalidate prior checks, leading to authorization bypass, state manipulation, and constraint bypass.

How to Resolve

Before (Vulnerable)

// Vulnerable: balance checked before CPI that modifies it
pub fn process(ctx: Context<Process>) -> Result<()> {
    let balance = ctx.accounts.user_token.amount;
    require!(balance >= MIN_BALANCE, InsufficientBalance);

    // CPI modifies user_token balance -- original check is stale
    token::transfer(ctx.accounts.into_transfer_ctx(), balance)?;
    // balance variable is now stale
    Ok(())
}

After (Fixed)

// Fixed: perform CPI first, then validate post-state
pub fn process(ctx: Context<Process>) -> Result<()> {
    let amount = ctx.accounts.user_token.amount;
    token::transfer(ctx.accounts.into_transfer_ctx(), amount)?;

    // Reload account data after CPI
    ctx.accounts.user_token.reload()?;
    require!(ctx.accounts.user_token.amount == 0, TransferIncomplete);
    Ok(())
}

Example JSON Finding

{
  "detector": "anchor-constraint-toctou",
  "severity": "high",
  "confidence": 0.7,
  "message": "Account state read before CPI that modifies the same account -- TOCTOU race",
  "location": { "function": "process", "block": 0, "statement": 3 }
}

Detection Methodology

  1. State read tracking: Identifies account data reads and the variables they populate.
  2. CPI detection: Locates CPI calls that reference accounts from which state was previously read.
  3. Stale usage detection: Flags usage of pre-CPI variable values after the CPI executes.
  4. Account overlap analysis: Determines which CPI calls could modify which accounts.

Limitations

False positives: CPI calls to programs that do not modify the checked account data may be flagged. False negatives: Complex state dependencies through multiple intermediary accounts may not be traced.

References