lang: Fix unexpected account substitution in InterfaceAccount#4139
Merged
jacobcreech merged 8 commits intoJan 5, 2026
Conversation
|
@acheroncrypto is attempting to deploy a commit to the Solana Foundation Team on Vercel. A member of the Team first needs to authorize it. |
Collaborator
Author
|
Before the fix commit (CI): After (CI): |
Collaborator
Author
|
Another related problem: the owner check in I think this behavior is inconsistent with Perhaps we could add another method (something like |
jamie-osec
approved these changes
Jan 5, 2026
jacobcreech
approved these changes
Jan 5, 2026
Otter-0x4ka5h
pushed a commit
to Otter-0x4ka5h/anchor
that referenced
this pull request
Mar 25, 2026
…r-sec#4139) * tests: Add basic security checks tests for `InterfaceAccount` * ci: Add `interface-account` tests * lang: Fix `InterfaceAccount::try_from` * lang: Fix `InterfaceAccount::reload` * lang: Allow multiple owners in `InterfaceAccount::reload` * lang: Remove the unnecessary clone to get the account info * lang: Fix compile error about mutable borrow * lang: Remove the unused imports
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
InterfaceAccountallows account substitution between unexpected types.PoC
Passing
AnotherAccountto an account typed asInterfaceAccount<ExpectedAccount>:Details
The PR titled "feat(account): Check Owner on Reload" #3837 changed
InterfaceAccount::try_fromfrom:https://github.com/solana-foundation/anchor/blob/2da41204735c944686a39bd18a50a0ff173425bb/lang/src/accounts/account.rs#L303-L313
to:
https://github.com/solana-foundation/anchor/blob/3a799e2d32bd708f0b280c60baecac76418248b1/lang/src/accounts/interface_account.rs#L243-L252
The author assumes
InterfaceAccountis only intended to work with programs that "do not have Anchor discriminators". However, similar toAccount, theInterfaceAccountimplementation does not actually make any assumptions about discriminators. As its name suggests, it's for accounts that share the same interface between different programs; or in other words, it's the same as theAccounttype, but instead of checking only a single owner via theOwnertrait, it allows multiple owners via theOwnerstrait.Similar to the
Accounttype,InterfaceAccount's documentation even has a section called UsingInterfaceAccountwith non-anchor programs that starts with:https://github.com/solana-foundation/anchor/blob/3a799e2d32bd708f0b280c60baecac76418248b1/lang/src/accounts/interface_account.rs#L85
which in itself should be enough to suggest that
InterfaceAccountcan also be used with Anchor program accounts, or generally, accounts that have discriminators.The same erroneous understanding also resulted in changing the
reloadmethod ofAccountInterfacefrom:https://github.com/solana-foundation/anchor/blob/2da41204735c944686a39bd18a50a0ff173425bb/lang/src/accounts/interface_account.rs#L188
which used
Account::reloadwith checked account deserialization (AccountDeserialize::try_deserialize):https://github.com/solana-foundation/anchor/blob/2da41204735c944686a39bd18a50a0ff173425bb/lang/src/accounts/account.rs#L271
to an implementation that uses unchecked account deserialization (
AccountDeserialize::try_deserialize_unchecked):https://github.com/solana-foundation/anchor/blob/3a799e2d32bd708f0b280c60baecac76418248b1/lang/src/accounts/interface_account.rs#L206
The misconception might have arisen from the fact that
InterfaceAccountwas initially added (in #2386) in order to make handling SPL Token and SPL Token 2022 accounts easier. However, its implementation is fully generic, just like theAccounttype. In fact, in its implementation PR, the first commit is titled 'Add "interface" and "interface account" concept', and it does not even touchanchor-spl.The reason why
anchor-spltypes such astoken::Mintandtoken_interface::Mintonly implementtry_deserialize_uncheckedis becausetry_deserializedefaults totry_deserialize_unchecked(in this case they are all checked in reality):https://github.com/solana-foundation/anchor/blob/3a799e2d32bd708f0b280c60baecac76418248b1/lang/src/lib.rs#L361-L370
This means changing
try_deserializetotry_deserialize_uncheckedhere in the best case has no benefits (same impl), and in the worst case allows bypassing account checks.Summary of changes
InterfaceAccount, including unexpected account substitutionInterfaceAccount::try_from(revert to how it was before)InterfaceAccount::reload(use checked account deserialization)InterfaceAccount::reload(see lang: Fix unexpected account substitution inInterfaceAccount#4139 (comment))