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
- Bump tracking: Maps bump variables to their usage locations and associated seed patterns.
- Pattern comparison: Detects when the same bump variable is used with different seed patterns (different lengths or different variable references).
- Hardcoded bump detection: Separately flags constant bump values reused across multiple PDA derivations.
- 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.
Related Detectors
- Bump Seed Canonicalization — detects non-canonical bump seeds
- Bump Seed Brute Force — detects user-controlled bump seeds