◂ labs
TIER 2 · CPI & PDAs · 7-bump-seed-canonicalization
BUMP SEED CANONICALIZATION
A PDA can be derived with multiple valid bumps, but only one is canonical. Accepting a non-canonical bump lets an attacker derive a second address for the "same" PDA — two accounts where the program expects one.
seen in the wild
Subtle but real: non-canonical PDAs break the one-account-one-vault invariant protocols rely on.
rust · anchor
use anchor_lang::prelude::*;
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
pub mod bump_seed_canonicalization_insecure {
use super::*;
pub fn set_value(ctx: Context<BumpSeed>, key: u64, new_value: u64, bump: u8) -> ProgramResult {
let address =
Pubkey::create_program_address(&[key.to_le_bytes().as_ref(), &[bump]], ctx.program_id)?;
if address != ctx.accounts.data.key() {
return Err(ProgramError::InvalidArgument);
}
ctx.accounts.data.value = new_value;
Ok(())
}
}
#[derive(Accounts)]
pub struct BumpSeed<'info> {
data: Account<'info, Data>,
}
#[account]
pub struct Data {
value: u64,
}
next labPDA SHARING ▸