Introduction
Greetings, developers!
We’re Neodyme, a security research team specializing in Solana blockchain audits. Over the past year, we’ve uncovered critical vulnerabilities in Solana’s core code and smart contracts, safeguarding roughly $1 billion in assets. While manual audits remain essential, we’re sharing our top findings to help you write more secure contracts.
Below, we detail the five most frequent vulnerabilities in Solana programs, complete with simplified examples and actionable fixes.
1. Missing Ownership Check
TL;DR
🔹 Always verify the AccountInfo::owner
field for non-user-controlled accounts.
🔹 Use helper functions to validate ownership and return trusted types.
Description
Solana accounts include an owner
field designating which program can modify their data. Failing to validate ownership lets attackers supply malicious accounts.
Example
fn withdraw_token_restricted(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
let config = ConfigAccount::unpack(next_account_info(&mut accounts.iter())?)?;
// Missing check: if config.owner != program_id
Ok(())
}
Fix: Add if config.owner != program_id
to ensure the account is program-owned.
2. Missing Signer Check
TL;DR
🔹 Restrict sensitive instructions by verifying AccountInfo::is_signer
.
Description
Without signer checks, attackers can spoof admin or user accounts.
Example
fn update_admin(accounts: &[AccountInfo]) -> ProgramResult {
let admin = next_account_info(&mut accounts.iter())?;
// Missing check: if !admin.is_signer
Ok(())
}
Fix: Add if !admin.is_signer
to confirm transaction signing.
3. Integer Overflow/Underflow
TL;DR
🔹 Use checked_add
and TryFrom
to avoid wrapping arithmetic.
Description
Rust’s release mode (including BPF) silently wraps overflows.
Example
let amount: u32 = u32::MAX - 1000;
let total = amount + 1000; // Wraps to 899!
Fix: Replace +
with amount.checked_add(FEE)?.ok_or(ProgramError::InvalidArgument)?
.
4. Arbitrary Program Invocation
TL;DR
🔹 Always validate program IDs before invoke_signed
.
Description
Users can supply malicious program clones.
Example
invoke_signed(&malicious_transfer_instruction, accounts, seeds)?;
Fix: Add if token_program.key != &spl_token::id()
.
5. Account Type Confusion
TL;DR
🔹 Add TYPE
fields to structs and validate during deserialization.
Description
Raw account data lacks type safety.
Example
pub struct User {
pub TYPE: u8, // Set to 1
pub balance: u64,
}
pub struct Config {
pub TYPE: u8, // Set to 2
pub admin: Pubkey,
}
Fix: Reject accounts where TYPE
mismatches.
FAQs
Q1: How do I securely validate account ownership?
A1: Use a helper function that checks owner
and returns a trusted wrapper type.
Q2: What’s the risk of missing signer checks?
A2: Attackers can bypass admin restrictions by spoofing pubkeys.
Q3: Why avoid as
for integer casts?
A3: It silently truncates. Use TryFrom
for safe conversions.
👉 Explore Solana’s official documentation for deeper insights.
Conclusion
These vulnerabilities are just the tip of the iceberg. Always audit thoroughly and stay updated with Solana’s security advisories. For more advanced pitfalls, watch this space!
👉 Learn about Solana’s SPL token standards to enhance your contract security.