◂ labs
TIER 2 · CPI & PDAs · 4-initialization
REINITIALIZATION
An `init`-style instruction callable twice on the same account resets authority/data to attacker-controlled values. `init_if_needed` is the classic footgun: "needed" is decided by the caller, and a re-init overwrites the admin.
seen in the wild
Reinit bugs killed several early Solana protocols; Anchor 0.25+ made `init_if_needed` require explicit opt-in flags for a reason.
rust · anchor
use anchor_lang::prelude::*;
use borsh::{BorshDeserialize, BorshSerialize};
use std::ops::DerefMut;
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
pub mod initialization_insecure {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> ProgramResult {
let mut user = User::try_from_slice(&ctx.accounts.user.data.borrow()).unwrap();
user.authority = ctx.accounts.authority.key();
let mut storage = ctx.accounts.user.try_borrow_mut_data()?;
user.serialize(storage.deref_mut()).unwrap();
Ok(())
}
}
/*
- reinitialize
- create and dont initialize
- passing previously initialzed accounts from other programs
(e.g. token program => need to check delegate and authority)
*/
#[derive(Accounts)]
pub struct Initialize<'info> {
user: AccountInfo<'info>,
authority: Signer<'info>,
}
#[derive(BorshSerialize, BorshDeserialize)]
pub struct User {
authority: Pubkey,
}
next labARBITRARY CPI ▸