Skip to main content
Sigvex

PDA Path Confusion Remediation

How to fix PDA derivations lacking program-specific namespace seeds.

PDA Path Confusion Remediation

Overview

Related Detector: PDA Path Confusion

PDA path confusion occurs when PDA derivations use only user-controlled seeds without a program-specific namespace. The fix is to include a constant string discriminator as the first seed component.

Before (Vulnerable)

let (pda, _) = find_program_address(&[user.key.as_ref()], program_id);

After (Fixed)

let (pda, _) = find_program_address(&[b"vault", user.key.as_ref()], program_id);

Alternative Mitigations

1. Version-qualified namespaces

Include a version identifier for forward compatibility:

let (pda, _) = find_program_address(&[b"vault-v2", user.key.as_ref()], program_id);

2. Program ID as seed for cross-program PDAs

let (pda, _) = find_program_address(
    &[b"cross-ref", source_program.as_ref(), user.key.as_ref()],
    target_program_id,
);

3. Account type discriminators

Use different prefixes for each account type:

let (user_pda, _) = find_program_address(&[b"user", user.key.as_ref()], pid);
let (vault_pda, _) = find_program_address(&[b"vault", user.key.as_ref()], pid);
let (config_pda, _) = find_program_address(&[b"config"], pid);

Common Mistakes

Mistake 1: Using only numeric prefixes

// WEAK: single byte does not prevent accidental collision
let (pda, _) = find_program_address(&[&[1u8], user.key.as_ref()], pid);

Use descriptive string literals instead of numeric constants.

Mistake 2: Using user-controlled data as the namespace

// WRONG: user-controlled data is not a namespace
let prefix = &instruction_data[0..8]; // Attacker-controlled
let (pda, _) = find_program_address(&[prefix, user.key.as_ref()], pid);

References