diff --git a/CHANGELOG.md b/CHANGELOG.md index c93f47e583..49e764c3b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,7 @@ The minor version will be incremented upon a breaking change and the patch versi - avm: Add Windows support for renaming anchor binary ([#3325](https://github.com/coral-xyz/anchor/pull/3325)). - cli: Add optional `package-manager` flag in `init` command to set package manager field in Anchor.toml ([#3328](https://github.com/coral-xyz/anchor/pull/3328)). - cli: Add test template for [Mollusk](https://github.com/buffalojoec/mollusk) ([#3352](https://github.com/coral-xyz/anchor/pull/3352)). +- idl: Disallow account discriminators that can conflict with the `zero` constraint ([#3365](https://github.com/coral-xyz/anchor/pull/3365)). ### Fixes diff --git a/idl/src/build.rs b/idl/src/build.rs index d6dd9c2477..c5dbe4a91a 100644 --- a/idl/src/build.rs +++ b/idl/src/build.rs @@ -404,5 +404,35 @@ fn verify(idl: &Idl) -> Result<()> { )); } + // Disallow account discriminators that can conflict with the `zero` constraint. + // + // Problematic scenario: + // + // 1. Account 1's discriminator starts with 0 (but not all 0s, since that's disallowed) + // 2. Account 2's discriminator is a 1-byte custom discriminator + // 3. Account 2 gets initialized using the `zero` constraint. + // + // In this case, it's possible to pass an already initialized Account 1 to a place that expects + // non-initialized Account 2, because the first byte of Account 1 is also 0, which is what the + // `zero` constraint checks. + for account in &idl.accounts { + let zero_count = account + .discriminator + .iter() + .take_while(|b| **b == 0) + .count(); + if let Some(account2) = idl + .accounts + .iter() + .find(|acc| acc.discriminator.len() <= zero_count) + { + return Err(anyhow!( + "Accounts may allow substitution when used with the `zero` constraint: `{}` `{}`", + account.name, + account2.name + )); + } + } + Ok(()) }