◂ labs
TIER 2 · CPI & PDAs · 6-duplicate-mutable-accounts

DUPLICATE MUTABLE ACCOUNTS

Pass the same account twice in the accounts array and the runtime hands your handler two mutable references to one account. Debit it as "A" and "B" — you just debited twice the same balance.

seen in the wild
Classic double-spend primitive; the fix is a one-line key equality check Anchor does via constraints.
rust · anchor
use anchor_lang::prelude::*;

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

#[program]
pub mod duplicate_mutable_accounts_insecure {
    use super::*;

    pub fn update(ctx: Context<Update>, a: u64, b: u64) -> ProgramResult {
        let user_a = &mut ctx.accounts.user_a;
        let user_b = &mut ctx.accounts.user_b;

        user_a.data = a;
        user_b.data = b;
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Update<'info> {
    user_a: Account<'info, User>,
    user_b: Account<'info, User>,
}

#[account]
pub struct User {
    data: u64,
}