Skip to content

Commit

Permalink
Update arbitrary_cpi lint to check for Instruction{..} and CpiContext…
Browse files Browse the repository at this point in the history
…::new, CpiContext::new_with_signer calls
  • Loading branch information
Vara Prasad Bandaru committed Mar 8, 2024
1 parent 7210d7e commit 3a33c4c
Show file tree
Hide file tree
Showing 6 changed files with 365 additions and 343 deletions.
9 changes: 8 additions & 1 deletion crate/src/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub const ANCHOR_LANG_ACCOUNT: [&str; 4] = ["anchor_lang", "accounts", "account"
pub const ANCHOR_LANG_ACCOUNT_LOADER: [&str; 4] =
["anchor_lang", "accounts", "account_loader", "AccountLoader"];
pub const ANCHOR_LANG_PROGRAM: [&str; 4] = ["anchor_lang", "accounts", "program", "Program"];
pub const ANCHOR_LANG_INTERFACE: [&str; 4] = ["anchor_lang", "accounts", "interface", "Interface"];
pub const ANCHOR_LANG_SYSTEM_ACCOUNT: [&str; 4] =
["anchor_lang", "accounts", "system_account", "SystemAccount"];
pub const ANCHOR_LANG_ACCOUNT_DESERIALIZE: [&str; 2] = ["anchor_lang", "AccountDeserialize"];
Expand All @@ -20,7 +21,11 @@ pub const ANCHOR_LANG_TRY_DESERIALIZE: [&str; 3] =
// key() method call path
pub const ANCHOR_LANG_KEY: [&str; 3] = ["anchor_lang", "Key", "key"];
pub const ANCHOR_LANG_TO_ACCOUNT_INFOS_TRAIT: [&str; 2] = ["anchor_lang", "ToAccountInfos"];

// CpiContext::new()
pub const ANCHOR_CPI_CONTEXT_NEW: [&str; 4] = ["anchor_lang", "context", "CpiContext", "new"];
// CpiContext::new_with_signer()
pub const ANCHOR_CPI_CONTEXT_NEW_SIGNER: [&str; 4] =
["anchor_lang", "context", "CpiContext", "new_with_signer"];
pub const BORSH_TRY_FROM_SLICE: [&str; 4] = ["borsh", "de", "BorshDeserialize", "try_from_slice"];

pub const CORE_BRANCH: [&str; 5] = ["core", "ops", "try_trait", "Try", "branch"];
Expand All @@ -29,6 +34,8 @@ pub const CORE_CLONE: [&str; 4] = ["core", "clone", "Clone", "clone"];
pub const SOLANA_PROGRAM_ACCOUNT_INFO: [&str; 3] =
["solana_program", "account_info", "AccountInfo"];
pub const SOLANA_PROGRAM_INVOKE: [&str; 3] = ["solana_program", "program", "invoke"];
// Instruction {..}
pub const SOLANA_PROGRAM_INSTRUCTION: [&str; 3] = ["solana_program", "instruction", "Instruction"];
pub const SOLANA_PROGRAM_CREATE_PROGRAM_ADDRESS: [&str; 4] = [
"solana_program",
"pubkey",
Expand Down
38 changes: 19 additions & 19 deletions lints/arbitrary_cpi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,22 @@ invoke(&ix, accounts.clone());

**How the lint is implemented:**

- For every function containing calls to `solana_program::program::invoke`
- find the definition of `Instruction` argument passed to `invoke`; first argument
- If the `Instruction` argument is result of a function call
- If the function is whitelisted, do not report; only functions defined in
`spl_token::instruction` are whitelisted.
- Else report the call to `invoke` as vulnerable
- Else if the `Instruction` is initialized in the function itself
- find the assign statement assigning to the `program_id` field, assigning to
field at `0`th index
- find all the aliases of `program_id`. Use the rhs of the assignment as initial
alias and look for all assignments assigning to the locals recursively.
- If `program_id` is compared using any of aliases ignore the call to `invoke`.
- Look for calls to `core::cmp::PartialEq{ne, eq}` where one of arg is moved
from an alias.
- If one of the arg accesses `program_id` and if the basic block containing the
comparison dominates the basic block containing call to `invoke` ensuring the
`program_id` is checked in all execution paths Then ignore the call to `invoke`.
- Else report the call to `invoke`.
- Else report the call to `invoke`.
- For every function
- For every statement in the function initializing `Instruction {..}`
- Get the place being assigned to `program_id` field
- find all the aliases of `program_id`. Use the rhs of the assignment as initial
alias and look for all assignments assigning to the locals recursively.
- If `program_id` is compared using any of aliases ignore the call to `invoke`.
- Look for calls to `core::cmp::PartialEq{ne, eq}` where one of arg is moved
from an alias.
- If one of the arg accesses `program_id` and if the basic block containing the
comparison dominates the basic block containing call to `invoke` ensuring the
`program_id` is checked in all execution paths Then ignore the call to `invoke`.
- Else report the statement initializing `Instruction`.
- Else report the statement initializing `Instruction`.
- For every call to `CpiContext::new` or `CpiContext::new_with_signer`
- Get the place of the first argument (program's account info)
- find all aliases of `program's` place.
- If the `program` is a result of calling `to_account_info` on Anchor `Program`/`Interface`
- continue
- Else report the call to `CpiContext::new`/`CpiContext::new_with_signer`
Loading

0 comments on commit 3a33c4c

Please sign in to comment.