Skip to main content
Sigvex

PDA System Collision

Detects PDA derivations with weak entropy that could collide with system program addresses.

PDA System Collision

Overview

Remediation Guide: How to Fix PDA System Collision

The PDA system collision detector identifies PDA derivations with weak seed entropy that could potentially collide with known system program addresses (System Program, Token Program, Token-2022, Associated Token Program, Memo Program, BPF Loader, the Metaplex Token Metadata program). While collisions are mathematically unlikely in a 256-bit address space, weak seed patterns combined with maliciously crafted program IDs could intentionally target specific addresses.

The detector flags three escalating patterns:

  1. Single constant seed: Minimal entropy, entirely deterministic based on program ID (Critical).
  2. Weak entropy seeds: Single variable or parameter seeds without discriminators (High).
  3. Missing canonical bump: create_program_address called without bump seed from find_program_address (Medium).

Why This Is an Issue

A PDA derived from a single constant or a single user-provided value has extremely low entropy. An attacker could craft a program ID that, combined with the weak seed, produces a PDA matching a system program address. This could bypass program ID validation checks that verify the account key but not the owner, enabling impersonation of system programs in CPI contexts.

CWE mapping: CWE-330 (Use of Insufficiently Random Values).

How to Resolve

Native Solana

// VULNERABLE: single constant seed
let pda = create_program_address(&[&[0]], program_id)?;

// FIXED: multiple seeds with discriminator and bump
let (pda, bump) = find_program_address(&[b"vault", user.key.as_ref()], program_id);
let pda = create_program_address(&[b"vault", user.key.as_ref(), &[bump]], program_id)?;

Anchor

#[account(seeds = [b"vault", user.key().as_ref()], bump)]
pub vault: Account<'info, Vault>,

Examples

Vulnerable Code

pub fn init(accounts: &[AccountInfo], program_id: &Pubkey) -> ProgramResult {
    // Single constant -- Critical: entirely program-ID dependent
    let pda = Pubkey::create_program_address(&[&[42]], program_id)?;
    Ok(())
}

Fixed Code

pub fn init(accounts: &[AccountInfo], program_id: &Pubkey) -> ProgramResult {
    let (pda, bump) = Pubkey::find_program_address(
        &[b"vault", user.key.as_ref(), mint.key.as_ref()],
        program_id,
    );
    Ok(())
}

Sample Sigvex Output

{
  "detector_id": "pda-system-collision",
  "severity": "critical",
  "confidence": 0.78,
  "description": "PDA derivation uses only a single constant value as seed. This provides minimal entropy and makes the derivation entirely deterministic based on program ID.",
  "location": { "function": "init", "block": 0, "stmt": 0 }
}

Detection Methodology

  1. PDA detection: Identifies find_program_address and create_program_address syscalls.
  2. Entropy analysis: Classifies seed strength by counting components and their types (constants, variables, parameters, loads).
  3. Single-constant detection: Flags seeds that consist of only one constant value as critical.
  4. Bump seed check: For create_program_address, checks whether a bump seed is likely present.
  5. System address awareness: Reports against the list of known critical system addresses.

Limitations

  • The detector cannot actually verify whether a collision with a system address is mathematically possible for a given seed pattern.
  • Entropy analysis is heuristic; complex seed expressions may be under- or over-counted.
  • The bump seed check is conservative and may flag legitimate create_program_address calls that include bump seeds in complex expressions.

References