Integer Overflow Remediation (SVM)
How to prevent integer overflow and underflow in Solana programs by using checked arithmetic methods and the Anchor checked_math! macro.
Integer Overflow Remediation (SVM)
Overview
Rust release builds use wrapping arithmetic by default — overflow silently wraps to zero or maximum rather than panicking. Solana programs compiled in release mode are therefore vulnerable to integer overflow on user-controlled values unless checked arithmetic is used explicitly. The remediation is to replace all arithmetic on user-controlled financial values with checked_add, checked_sub, checked_mul, or Anchor’s checked_math! macro.
Related Detector: Integer Overflow (SVM)
Recommended Fix
Before (Vulnerable)
// Wrapping arithmetic in release mode — silent overflow
pub fn deposit(vault: &mut Vault, amount: u64) -> ProgramResult {
vault.balance = vault.balance + amount; // Silently wraps in release
Ok(())
}
After (Fixed)
use solana_program::program_error::ProgramError;
// Checked arithmetic — returns error on overflow
pub fn deposit(vault: &mut Vault, amount: u64) -> ProgramResult {
vault.balance = vault.balance
.checked_add(amount)
.ok_or(ProgramError::ArithmeticOverflow)?;
Ok(())
}
Alternative Mitigations
Anchor checked_math! macro — wraps multiple arithmetic operations in a single checked block:
use anchor_lang::prelude::*;
pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
let vault = &mut ctx.accounts.vault;
// checked_math! wraps all enclosed operations — any overflow returns Err
vault.balance = checked_math!(vault.balance + amount)?;
// Also works for complex expressions
let fee = checked_math!(amount * FEE_RATE / FEE_BASIS)?;
vault.accumulated_fees = checked_math!(vault.accumulated_fees + fee)?;
Ok(())
}
Saturating arithmetic — for counters and reputation scores where clamping to maximum is acceptable semantics:
// Only for non-financial values where clamping is acceptable
score = score.saturating_add(points); // Clamps to u64::MAX rather than wrapping
Explicit bounds check — for cases where the business logic requires rejecting out-of-range values:
const MAX_DEPOSIT: u64 = 1_000_000_000; // 1B lamports
pub fn deposit(vault: &mut Vault, amount: u64) -> ProgramResult {
require!(amount <= MAX_DEPOSIT, MyError::DepositTooLarge);
vault.balance = vault.balance
.checked_add(amount)
.ok_or(ProgramError::ArithmeticOverflow)?;
Ok(())
}
Common Mistakes
Using + or - in release builds — Rust’s + operator wraps in release mode. Always use checked_add / checked_sub for financial values.
Mixing checked and unchecked in the same function — if one operation is unchecked, overflow can still propagate into subsequent checked operations as a normal value.
saturating_add for financial balances — saturating at u64::MAX is not a valid outcome for a token balance. Use checked arithmetic and return an error instead.