Integer Overflow (SVM)
Detects unchecked arithmetic operations on user-controlled values in Solana programs that may overflow or underflow, enabling fund manipulation.
Integer Overflow (SVM)
Overview
Remediation Guide: How to Fix Integer Overflow (SVM)
The SVM integer overflow detector identifies arithmetic operations on user-controlled values in Solana programs where overflow or underflow can occur without producing an error. Rust’s default release-build semantics perform wrapping arithmetic — no panic on overflow. Solana programs compiled in release mode therefore silently wrap u64::MAX + 1 to 0 unless the developer explicitly uses checked (checked_add, checked_sub) or saturating variants.
The detector performs taint-propagation analysis: all function parameters and account data reads (Load) are treated as user-controlled. Any arithmetic operation (Add, Sub, Mul) on tainted operands that is not protected by a checked variant is flagged. Loop counters are filtered to reduce false positives. Confidence is adjusted downward for Anchor programs (which conventionally use checked_math!) and read-only functions.
Why This Is an Issue
Rust’s release build wrapping arithmetic is a deliberate design choice for performance, but it means financial logic that operates on user-supplied amounts is vulnerable without explicit protection. An attacker who supplies a crafted amount close to u64::MAX can cause the sum of a balance and the supplied amount to wrap to a small value, bypassing balance checks or minting unlimited tokens.
Unlike Solidity’s pre-0.8 overflow, Rust wrapping arithmetic does not panic — the program completes successfully with a corrupted value. This makes the attack invisible in logs: the transaction succeeds and the resulting state appears valid until the corrupted value is used.
How to Resolve
Replace all arithmetic on user-controlled financial values with checked variants. Return an error when overflow is detected. Use Anchor’s checked_math! macro to wrap multiple arithmetic operations in a single checked block.
// Before: Vulnerable — raw arithmetic on user-controlled amount
pub fn deposit(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
let mut data = accounts[0].data.borrow_mut();
let existing_balance = u64::from_le_bytes(data[0..8].try_into().unwrap());
let new_balance = existing_balance + amount; // Wraps silently in release mode
data[0..8].copy_from_slice(&new_balance.to_le_bytes());
Ok(())
}
// After: Fixed — checked arithmetic
pub fn deposit(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
let mut data = accounts[0].data.borrow_mut();
let existing_balance = u64::from_le_bytes(data[0..8].try_into().unwrap());
let new_balance = existing_balance
.checked_add(amount)
.ok_or(ProgramError::ArithmeticOverflow)?;
data[0..8].copy_from_slice(&new_balance.to_le_bytes());
Ok(())
}
Examples
Vulnerable Code
use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult};
// VULNERABLE: raw arithmetic wraps in release mode
pub fn deposit(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
let vault = &accounts[0];
let mut data = vault.data.borrow_mut();
let existing_balance = u64::from_le_bytes(data[0..8].try_into().unwrap());
// In release mode: u64::MAX + 1 = 0, not an error
// Attacker supplies amount = u64::MAX - existing_balance + 1
// Result: new_balance = 0, but tokens are credited and can be withdrawn
let new_balance = existing_balance + amount;
data[0..8].copy_from_slice(&new_balance.to_le_bytes());
Ok(())
}
Fixed Code
use solana_program::{
account_info::AccountInfo,
entrypoint::ProgramResult,
program_error::ProgramError,
};
// FIXED: checked arithmetic — returns error on overflow
pub fn deposit(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
let vault = &accounts[0];
let mut data = vault.data.borrow_mut();
let existing_balance = u64::from_le_bytes(data[0..8].try_into().unwrap());
let new_balance = existing_balance
.checked_add(amount)
.ok_or(ProgramError::ArithmeticOverflow)?;
data[0..8].copy_from_slice(&new_balance.to_le_bytes());
Ok(())
}
// FIXED: Anchor with checked_math! macro
use anchor_lang::prelude::*;
pub fn deposit_anchor(ctx: Context<Deposit>, amount: u64) -> Result<()> {
let vault = &mut ctx.accounts.vault;
// checked_math! wraps all enclosed arithmetic in checked variants
vault.balance = checked_math!(vault.balance + amount)?;
Ok(())
}
Sample Sigvex Output
{
"detector_id": "integer-overflow",
"severity": "high",
"confidence": 0.72,
"description": "Add operation on user-controlled variable (function parameter amount) at statement index 4 in function deposit. No checked_add or saturating_add variant is used. Wrapping overflow is possible in release mode.",
"location": {
"function": "deposit",
"offset": 0
}
}
Detection Methodology
The detector performs a taint-propagation analysis focused on security-relevant arithmetic:
- User-controlled variable identification: All function parameters are treated as user-controlled. Any variable assigned from a
Loadexpression (account data read) is also user-controlled. Taint propagates through assignments. - Loop block filtering: Identifies loops with simple counter increments (
i < Npattern) and excludes their arithmetic from analysis to reduce false positives. - Security-relevant arithmetic detection: Scans for
HirExpr::BinOpnodes withAdd,Sub, orMuloperators. Reports only when at least one operand is user-controlled. - Confidence adjustment: Anchor programs receive confidence × 0.85; read-only functions receive confidence × 0.50.
Limitations
False positives:
- Arithmetic on values that are bounded by a prior
require!orifcheck may be flagged if the constraint does not propagate through the taint analysis. - Arithmetic on protocol-controlled constants that happen to be passed as parameters (e.g., fee rates set by admin) may be incorrectly flagged as user-controlled.
False negatives:
- Overflow via multiple operations (e.g., two consecutive
+each belowu64::MAX/2) where each individual operation is safe but the chain overflows is not detected. - Overflow in complex expressions involving struct field access patterns not yet fully modeled in the HIR.
Related Detectors
- Lamport Drain — detects unauthorized lamport transfers that may exploit arithmetic bugs
- Missing Signer Check — detects missing authorization before operations that may involve overflow-prone arithmetic