Solana Scanner Skill

SkillSecurity

Use when auditing Solana programs for security vulnerabilities, reviewing Anchor or Pinocchio/native Rust smart contracts, checking CPI safety, PDA validation, account ownership, signer verification, or Token-2022 security.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Solana Scanner Skill skill

What this skill tells your AI

The instructions your AI receives, as published by 0x-shashi/web3-audit-skills in skills/solana-scanner/SKILL.md and read by ahel’s review.

Purpose

Analyze Solana programs (Rust/Anchor) for security vulnerabilities specific to the Solana runtime model. Solana's account-based execution model, where programs are stateless and all state is passed via accounts, creates a fundamentally different attack surface from EVM chains.

Core Principle

An attacker controls every account, argument, ordering, and CPI call-graph passed to your program. Your on-chain code must prove each input is legitimate before touching state or funds.

Solana Security Model

PropertySolanaEVM
Execution modelPrograms receive accounts as inputContracts own their storage
State ownershipAccount owner (program ID) controls dataContract controls its own storage
Caller identitySigner flag on accountsmsg.sender
Cross-program callsCPI — accounts passed throughInternal calls share storage
Math safetyOverflow wraps in release modeSolidity 0.8+ reverts on overflow
Account validationManual (native) or declarative (Anchor)Automatic via msg.sender
UpgradesProgram authority can upgrade any timeProxy patterns required
RentAccounts must maintain minimum SOL balanceNo rent (storage is permanent)

Detection Capabilities

Critical — Direct Fund Loss

VulnerabilityDescriptionDetection Signal
Missing signer checkPrivileged instruction lacks signer validationAccountInfo without is_signer check
Missing owner checkProgram accepts accounts owned by other programsNo owner == program_id validation
Arbitrary CPICross-program invocation to user-controlled program IDinvoke() with unchecked program_id
PDA seed manipulationPDA derived with controllable seedsSeeds include user-controlled data without validation
Account data overwriteWriting data to wrong accountMissing discriminator / account type check

High — Significant Impact

VulnerabilityDescriptionDetection Signal
Integer overflow (release)Wrapping arithmetic in release buildsMath ops without checked_* or Anchor require!
Account closing revivalClosed account can be revived in same txClose without zeroing data + relying on zero lamports
Duplicate account injectionSame account passed for two different parametersNo uniqueness check between accounts
Type confusionAccount deserialized as wrong typeMissing discriminator validation
CPI privilege escalationCPI inherits signer privileges incorrectlyinvoke_signed() with wrong seeds

Medium — Conditional Impact

VulnerabilityDescriptionDetection Signal
PDA bump seed guessingNot storing/reusing canonical bumpfind_program_address in instruction logic
Missing rent exemptionAccount may be garbage collectedNo rent-exempt check after creation
Unchecked account sizeAccount realloc without boundsrealloc() without size validation
Clock dependencyUsing Clock::get() for security-sensitive logicValidator can influence timestamp slightly
Token account validationSPL Token account not validated for mint/ownerMissing token::mint or token::authority check

Key Audit Patterns

Vulnerability Categories with Prevention Code

Each category shows the risk, an attack example, and prevention for both Anchor and Pinocchio/native Rust.

1. Missing Owner Checks

Risk: Attacker substitutes a fake account owned by their own program with crafted data.

// Anchor Prevention — Account<'info, T> auto-checks owner
#[account]
pub struct Vault { pub authority: Pubkey, pub balance: u64 }

#[derive(Accounts)]
pub struct Withdraw<'info> {
    #[account(mut, has_one = authority)]
    pub vault: Account<'info, Vault>,  // owner = this program ✓
    pub authority: Signer<'info>,
}
// Pinocchio Prevention — explicit owner check
assert!(vault.is_owned_by(&crate::ID), ProgramError::IllegalOwner);
2. Missing Signer Checks

Risk: Anyone can call privileged instructions (withdraw, admin config).

// Anchor: Signer<'info> enforces is_signer automatically
pub authority: Signer<'info>,

// Pinocchio: manual check
assert!(authority.is_signer(), ProgramError::MissingRequiredSignature);
3. Arbitrary CPI

Risk: Attacker passes a malicious program ID, redirecting CPI to their program.

// Anchor Prevention — Program<'info, T> validates executable + ID
pub token_program: Program<'info, Token>,

// Pinocchio Prevention — compare key against known constant
assert_eq!(token_program.key(), &spl_token::ID);
4. Reinitialization

Risk: Attacker re-calls initialize to overwrite authority/settings.

// Anchor: init constraint creates + sets discriminator in one atomic step
#[account(init, payer = user, space = 8 + Vault::INIT_SPACE)]
pub vault: Account<'info, Vault>,
// ⚠️ Avoid init_if_needed — lets attacker front-run with bad data

// Pinocchio: check discriminator before writing
let data = vault.try_borrow_data()?;
assert_eq!(data[0..8], [0u8; 8], "already initialized");
5. PDA Sharing

Risk: Multiple users share one PDA → state confusion, fund theft.

// ❌ Bad: seeds only use pool-level data
#[account(seeds = [b"pool", pool.mint.as_ref()], bump)]

// ✅ Good: include user-specific identifier
#[account(seeds = [b"position", pool.key().as_ref(), user.key().as_ref()], bump)]
6. Type Cosplay

Risk: Attacker passes account of type A where type B is expected (same size).

// Anchor auto-adds 8-byte discriminator to all #[account] types,
// and Account<'info, T> validates it on deserialization.

// Pinocchio: first 8 bytes must be a unique discriminator per type
let disc = &data[0..8];
assert_eq!(disc, VAULT_DISCRIMINATOR, "wrong account type");
7. Duplicate Mutable Accounts

Risk: Same account passed as both source and destination → double-counting.

// Both frameworks — explicit key comparison
require!(source.key() != destination.key(), ErrorCode::DuplicateAccounts);
8. Account Revival (Closing)

Risk: Closed account (0 lamports) data remains in tx — later instruction reads stale state.

// Anchor: close transfers lamports + zero discriminator
#[account(mut, close = destination)]
pub vault: Account<'info, Vault>,

// Pinocchio: manually zero + transfer + realloc
data[0] = 0xFF; // poison discriminator
**vault.try_borrow_mut_lamports()? = 0;
**destination.try_borrow_mut_lamports()? += lamports;
vault.realloc(0, false)?;
9. Data Matching

Risk: Token account's mint or authority doesn't match expected values.

// Anchor: has_one validates field matches account key
#[account(mut, has_one = authority, has_one = mint)]
pub token_acct: Account<'info, TokenAccount>,

// Pinocchio: manual comparison
assert_eq!(token_acct.authority, expected_authority.key());

Account Validation Matrix

Every instruction must validate EVERY account it accesses. Use this matrix:

CheckNative SolanaAnchor
Is signer?account.is_signerSigner<'info>
Is writable?account.is_writable#[account(mut)]
Correct owner?account.owner == &program_idAccount<'info, T> (automatic)
Correct PDA?Pubkey::find_program_address()#[account(seeds = [...], bump)]
Correct data type?Check discriminator manuallyAccount<'info, T> (automatic)
Belongs to user?Manual field comparison#[account(has_one = owner)]
Not closed?Check lamports > 0 and dataAccount<'info, T> (checks discriminator)
Unique?Compare pubkeys between paramsManual (Anchor doesn't auto-check)

Solana-Specific Attack Vectors

  1. Account Confusion Attack: Passing a token account where a mint account is expected (or vice versa)
  2. Reinitialization Attack: Calling initialize on already-initialized account
  3. Closing Account Revival: Closing an account (zero lamports) but data remains readable in same tx
  4. CPI Re-signer Attack: Exploiting that CPI can sign with PDA seeds the caller controls
  5. Remaining Accounts Abuse: Exploiting unchecked accounts in ctx.remaining_accounts
  6. Flashback Attack: Solana transactions can include multiple instructions — attacker can create and exploit account state in same tx

Program-Side Security Checklist

Account Validation (7 checks)

  • Every account has an owner check (native) or uses Account<'info, T> (Anchor)
  • Every privileged account has a signer check
  • PDAs are derived with sufficient unique seeds (no PDA sharing)
  • All accounts have discriminator validation (type cosplay prevention)
  • Semantically distinct accounts are compared for uniqueness
  • has_one / manual field comparison for data matching (mint, authority)
  • Accounts in remaining_accounts are validated before use

CPI Safety (3 checks)

  • CPI target is a known program (Program<'info, T> or key comparison)
  • invoke_signed seeds are deterministic and cannot be replayed
  • CPI return data is validated, not blindly trusted

Arithmetic (3 checks)

  • All math uses checked_* operations or overflow-checks = true in Cargo.toml
  • Token amounts validated against actual balances before transfer
  • Fee/reward calculations handle rounding and zero-value edge cases

State Lifecycle (4 checks)

  • init is used instead of init_if_needed (prevents front-running)
  • Closed accounts are zeroed + poisoned (discriminator = 0xFF)
  • Upgrade authority is verified or program is immutable in production
  • Account realloc operations check size bounds

Client-Side Security Checklist

  • Correct cluster (mainnet vs devnet) — program IDs differ
  • Transactions are simulated before sending to catch errors early
  • getLatestBlockhash uses confirmed or finalized commitment
  • Transaction confirmation waits for sufficient commitment level
  • Token-2022 extensions (transfer hooks, transfer fees) are handled
  • Lookup tables / versioned transactions are used for large account sets
  • Priority fees are set appropriately for congested network conditions

Build & IDL Integrity Checklist

Supply-chain and deployment verification — catches mismatches between audited source and deployed program.

  • On-chain program matches audited source — verify with solana program dump + local rebuild
  • IDL matches on-chain behavior — generated IDL (Anchor/Codama) reflects actual instruction layouts
  • No hand-written Borsh layouts for programs the team owns — use IDL/codegen pipeline (hand-written layouts risk serialization mismatches → type confusion)
  • No hand-written IDLs — generate from source via anchor build or Shank macros
  • Codegen outputs checked into git for deterministic builds, or generated in CI with pinned versions
  • Anchor version pinned in Anchor.toml — different Anchor versions produce different discriminators
  • Solana CLI + Anchor versions aligned across dev, CI, and audit environments
  • Program upgrade authority is multisig or governance (not a single EOA) for mainnet deployments
  • If upgradeable, upgrade timelock exists to allow users to exit before code changes

Security Review Questions

When reviewing a Solana program, systematically ask:

  1. Can an attacker pass a fake account (wrong owner / wrong type)?
  2. Can a non-authorized user trigger this instruction?
  3. Can CPI be substituted to a malicious program?
  4. Can initialize be called again on an existing account?
  5. Are PDAs shared across users or contexts unintentionally?
  6. Can the same account be passed for two different params?
  7. Can a closed account be read or revived in a later instruction?
  8. Do on-chain fields match expected accounts (authority, mint, owner)?

Solana Audit Procedure

Step-by-step execution flow when auditing a Solana program. Adapted from Solana Foundation's recommended operating procedure.

Step 1: Classify the Program

QuestionWhy It Matters
Anchor or native/Pinocchio?Determines which constraint system to audit (declarative vs manual)
What token programs are used?SPL Token vs Token-2022 — different extension attack surfaces
Is the program upgradeable?If yes, audit the upgrade authority and governance process
Does it use CPI?Cross-program calls are the #1 privilege escalation vector
Are there multiple instructions per tx?Multi-instruction flows enable flashback attacks

Step 2: Map Every Account

For each instruction, build the Account Validation Matrix (see above). Every cell must be explicitly checked in code. An empty cell is a potential vulnerability.

Step 3: Trace CPI Chains

For each invoke() / invoke_signed():

  1. Is the target program ID validated? (Program<'info, T> or key comparison)
  2. Are signer seeds deterministic and non-replayable?
  3. Does the CPI return data get validated, or is it blindly trusted?
  4. Can an attacker substitute a malicious program ID?

Step 4: Verify Arithmetic Safety

  1. Check Cargo.toml for overflow-checks = true (release profile)
  2. If not present, every arithmetic operation must use checked_*
  3. Verify token amount calculations handle rounding and zero-value edge cases
  4. Check for division before multiplication (precision loss)

Step 5: Evaluate Formal Verification (if present)

If the program includes Kani proofs or other formal verification:

  1. Inventory proofs — count, classify (STRONG/WEAK/VACUOUS), categorize by domain
  2. Map proofs to security claims — does the proof actually back the claim?
  3. Identify what is NOT proven — CPI execution, oracle reads, and runtime behavior are never Kani-verifiable
  4. Check for coupling proofs — does the verify module match the production handler?
  5. Flag vacuous proofs — any proof with contradictory assume() chains is a finding
  6. Report strength — "N proofs (X% STRONG) covering [categories]"

See Formal Verification for Auditors for proof classification and evaluation methodology.

Step 6: Test with PoCs

Use the testing tools to write targeted PoCs for any findings:

  • LiteSVM — Fast in-process execution for exploit PoCs (seconds, not minutes)
  • Mollusk — Direct program execution for CU impact analysis
  • Surfpool — Integration testing against real mainnet state (Jupiter, Orca, etc.)

See Solana Testing for Auditors for setup guides.

Step 7: Deliverables

For each finding, provide:

  • Vulnerability category (from the 9 categories above)
  • Affected instruction(s) with account layout
  • Exploit scenario — step-by-step attack using concrete accounts
  • PoC code (if applicable) using LiteSVM or Mollusk
  • Fix recommendation with Anchor and/or native Rust code
  • Risk notes for anything touching signing, fees, CPIs, or token transfers

Workflows

WorkflowDescription
Anchor AuditAudit workflow for Anchor framework programs — constraint validation, CPI safety, PDA verification
Native AuditAudit workflow for native Solana programs — manual account deserialization, raw instruction processing

Resources

ResourceDescription
Account ValidationComplete guide to account validation checks: signer, owner, PDA, type, uniqueness
Anchor SecurityAnchor-specific security patterns: constraints, CPI, init_if_needed, close
Solana PatternsCommon vulnerability patterns with code examples and fixes
Curated Links50+ curated links to official docs, audit reports, security courses, tools, and firms
Security FundamentalsCore Solana security principles, threat model, and best practices
Native SecurityNative Solana (non-Anchor) security patterns and pitfalls
Security ChecklistsAudit and client-side checklists for Solana programs
CaveatsSolana-specific caveats, gotchas, and edge cases for auditors
Formal VerificationKani proof evaluation: classification, property categories, verify module extract-and-prove pattern
Adversarial Test DesignAttack-first test taxonomy, conservation invariant methodology, 10-category checklist

Notable Solana Security Incidents

IncidentDateRoot CauseLoss
Wormhole bridgeFeb 2022Missing signer verification on complete_wrapped$326M
Cashio stablecoinMar 2022Missing crate_collateral_tokens.mint validation$52M
Mango MarketsOct 2022Oracle price manipulation + account borrowing$116M
Crema FinanceJul 2022Fake tick account injection in CPI$8.8M
Slope walletAug 2022Private key logging in centralized server$4.1M
SolendVariousMultiple oracle and liquidation issuesVarious

Ecosystem Context

MetricValue
Smart contract languageRust
Primary frameworkAnchor (>90% of new projects)
Block time~400ms
Transaction modelMultiple instructions per transaction
Account size limit10MB
Compute budget200,000 compute units per instruction (adjustable to 1.4M)
Token standardSPL Token / Token-2022
NFT standardMetaplex Token Metadata

Integration with Other Skills

SkillConnection
patterns/Cross-reference Solana-specific patterns with Solodit database (limited but growing)
exploit-forensics/Wormhole, Cashio, Mango exploits provide forensic case studies
chain-guides/solana.mdChain-level context for Solana validator behavior, consensus, fees
attack-trees/Solana-specific attack trees (account confusion, CPI escalation)

Error Code Reference

Common Solana program error codes encountered during audits. These appear in transaction logs and simulation failures.

Anchor Framework Errors

Error CodeError NameMeaning
0x64 (100)InstructionMissingExpected instruction not found in transaction
0x65 (101)InstructionFallbackNotFoundNo fallback handler for instruction
0xBB8 (3000)ConstraintMutAccount not marked as mutable (#[account(mut)] missing)
0xBB9 (3001)ConstraintHasOnehas_one constraint failed — account field doesn't match
0xBBA (3002)ConstraintSignerAccount is not a signer
0xBBB (3003)ConstraintRawCustom constraint = <expr> evaluated to false
0xBBC (3004)ConstraintOwnerAccount owner does not match expected program
0xBBF (3007)ConstraintSeedsPDA seeds do not derive the expected address
0xBC4 (3012)ConstraintSpaceAccount data space insufficient
0x7D0 (2000)DeclaredProgramIdMismatchdeclare_id! does not match actual program ID
0x7D1 (2001)TryingToInitPayerAsProgramAccountPayer account reused as program-owned account
0xBCE (3022)AccountNotInitializedAccount data is empty / uninitialized
0xBC0 (3008)ConstraintExecutableAccount is not an executable program
0x1770 (6000)+Custom errorsApplication-specific errors start at 6000

SPL Token Program Errors

Error CodeError NameMeaning
0x0NotRentExemptAccount balance below rent-exempt minimum
0x1InsufficientFundsToken balance too low for transfer
0x3InvalidMintToken account mint does not match expected mint
0x4MintMismatchMint of token account does not match instruction mint
0x5OwnerMismatchToken account owner does not match expected owner
0xAAlreadyInUseAccount is already initialized
0xCInvalidNumberOfProvidedSignersWrong number of multisig signers
0xDInvalidNumberOfRequiredSignersInvalid multisig threshold
0x11AccountFrozenToken account is frozen — transfers blocked

System Program / Runtime Errors

Error CodeError NameMeaning
0x0InsufficientFundsForRentAccount will drop below rent-exempt after operation
0x1AccountAlreadyInitializedCannot reinitialize existing account
0x3AccountNotFoundReferenced account does not exist
ProgramFailedToComplete(Runtime)Program exceeded compute budget or panicked
PrivilegeEscalation(Runtime)CPI attempted to escalate privileges
AccountDataTooSmall(Runtime)Account data buffer too small for deserialization

Troubleshooting

IssueLikely CauseSolution
Scanner misses missing-signer vulnerabilitiesNative program uses AccountInfo without Anchor constraintsCheck all AccountInfo params for explicit is_signer / is_writable validation
False positive on PDA seedsScanner flags all user-controlled seedsVerify if seeds are bounded by program logic; canonical bump stored correctly
Account confusion not detectedDifferent account types share same structure sizeCheck discriminator bytes; Anchor auto-adds 8-byte discriminators
CPI privilege escalation missedinvoke_signed seeds not analyzedManually trace PDA seed derivation through CPI chain
Scanner doesn't flag overflow in release modeRelease builds wrap instead of panicFlag all non-checked_* math ops; verify overflow-checks = true in Cargo.toml
Duplicate account injection not caughtScanner doesn't check account uniquenessVerify all instruction accounts are compared for uniqueness where semantically distinct

Signals

GitHub stars
60
Forks
10
Last commit
Feb 2026
Advanced
Catalog kind
skill
Gateway key
solana-scanner
Source
github.com/0x-shashi/web3-audit-skills