Skip to main content
Sigvex

PDA Bump Seed Reuse

Detects reuse of the same bump seed value across multiple PDA derivations.

PDA Bump Seed Reuse

Overview

Remediation Guide: How to Fix PDA Bump Seed Reuse

The PDA bump seed reuse detector identifies when the same bump value is used across multiple PDA derivations with different seed patterns. Each PDA should have its own canonical bump derived from find_program_address. Reusing a single bump across different PDAs can cause invalid derivations, state confusion, or front-running attacks.

Why This Is an Issue

The canonical bump for a PDA is specific to its seed combination and program ID. A bump that is valid for [b"vault", user_key] is not necessarily valid for [b"metadata", user_key]. Reusing bumps can result in failed create_program_address calls, PDAs that accidentally resolve to the same address, or predictable addresses that enable front-running. Hardcoded constant bumps are especially dangerous as they bypass the canonicalization that find_program_address provides.

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

How to Resolve

Native Solana

// WRONG: reusing same bump for different PDAs
let bump = stored_data.bump;
let vault = create_program_address(&[b"vault", user.key, &[bump]], program_id)?;
let meta = create_program_address(&[b"metadata", user.key, &[bump]], program_id)?;

// CORRECT: derive separate canonical bumps
let (vault, vault_bump) = find_program_address(&[b"vault", user.key], program_id);
let (meta, meta_bump) = find_program_address(&[b"metadata", user.key], program_id);

Anchor

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

#[account(seeds = [b"metadata", user.key().as_ref()], bump)]
pub metadata: Account<'info, Metadata>,
// Each account gets its own bump automatically

Examples

Vulnerable Code

pub fn init_accounts(accounts: &[AccountInfo], bump: u8) -> ProgramResult {
    let vault = Pubkey::create_program_address(
        &[b"vault", user.key.as_ref(), &[bump]], program_id
    )?;
    let escrow = Pubkey::create_program_address(
        &[b"escrow", user.key.as_ref(), &[bump]], program_id // Same bump!
    )?;
    Ok(())
}

Fixed Code

pub fn init_accounts(accounts: &[AccountInfo]) -> ProgramResult {
    let (vault, vault_bump) = Pubkey::find_program_address(
        &[b"vault", user.key.as_ref()], program_id
    );
    let (escrow, escrow_bump) = Pubkey::find_program_address(
        &[b"escrow", user.key.as_ref()], program_id
    );
    // Store bumps separately
    state.vault_bump = vault_bump;
    state.escrow_bump = escrow_bump;
    Ok(())
}

Sample Sigvex Output

{
  "detector_id": "pda-bump-seed-reuse",
  "severity": "high",
  "confidence": 0.65,
  "description": "Bump seed variable v5 is reused across 2 different PDA derivations with different seed patterns.",
  "location": { "function": "init_accounts", "block": 0, "stmt": 0 }
}

Detection Methodology

  1. Bump tracking: Maps bump variables to their usage locations and associated seed patterns.
  2. Pattern comparison: Detects when the same bump variable is used with different seed patterns (different lengths or different variable references).
  3. Hardcoded bump detection: Separately flags constant bump values reused across multiple PDA derivations.
  4. Context adjustment: Confidence is reduced for Anchor programs (seeds constraint with stored bump prevents reuse).

Limitations

  • The detector tracks bump variables at the function level; bumps stored in account data and loaded in different functions are not correlated.
  • Bump reuse with identical seed patterns (safe) is correctly excluded, but complex seed expressions may not be fully compared.
  • The detector cannot determine if different bump values are intentionally derived from the same source.

References