Unchecked Arithmetic
Detects unchecked arithmetic operations that could silently overflow or underflow in SVM programs.
Unchecked Arithmetic
Overview
The unchecked arithmetic detector identifies addition, subtraction, multiplication, and division operations that do not use checked or saturating variants. In Solana’s BPF runtime, arithmetic overflow is undefined behavior in release mode — values wrap silently without error, producing incorrect results. This detector serves as a lightweight safety net, complementing the deeper integer-overflow detector. For remediation steps, see the Unchecked Arithmetic Remediation.
Why This Is an Issue
Rust programs compiled in release mode (as all deployed Solana programs are) do not panic on integer overflow — they wrap silently. This means:
u64::MAX + 1wraps to00u64 - 1wraps tou64::MAX(18,446,744,073,709,551,615)u64::MAX * 2wraps tou64::MAX - 1
For financial programs, these wrapping behaviors create exploitable vulnerabilities. A token amount that wraps from a large value to zero (or vice versa) causes incorrect transfers, balance corruption, or fund theft.
The detector filters common false positive patterns: loop counters (var + 1 in back-edge blocks), small constant offsets (struct field access, pointer arithmetic up to 4096), and constant-only arithmetic. It elevates severity to Medium when the arithmetic involves account data (token amounts) or state-critical operations (transfers, storage).
How to Resolve
// Before: Vulnerable -- unchecked addition
let total = amount_a + amount_b; // Wraps on overflow
// After: Fixed -- checked addition
let total = amount_a.checked_add(amount_b)
.ok_or(ProgramError::ArithmeticOverflow)?;
Examples
Vulnerable Code
pub fn calculate_reward(
base_amount: u64,
multiplier: u64,
bonus: u64,
) -> u64 {
// All three operations can overflow
let scaled = base_amount * multiplier;
let total = scaled + bonus;
total
}
Fixed Code
pub fn calculate_reward(
base_amount: u64,
multiplier: u64,
bonus: u64,
) -> Result<u64, ProgramError> {
let scaled = base_amount
.checked_mul(multiplier)
.ok_or(ProgramError::ArithmeticOverflow)?;
let total = scaled
.checked_add(bonus)
.ok_or(ProgramError::ArithmeticOverflow)?;
Ok(total)
}
Example JSON Finding
{
"detector": "unchecked-arithmetic",
"severity": "medium",
"confidence": 0.70,
"title": "Unchecked Arithmetic",
"description": "Unchecked multiplication operation that could overflow/underflow. Operand derived from account data.",
"cwe_ids": [190]
}
Detection Methodology
- Loop block identification: Maps blocks involved in loops (back-edge targets) to filter counter patterns.
- Compared variable tracking: Pre-scans branch conditions to identify variables that are compared after arithmetic, indicating potential overflow checks.
- Account data tracking: Identifies variables loaded from account data storage (likely token amounts or balances).
- Expression analysis: Checks arithmetic expressions against filter criteria (constant-only, loop counter, small offset) and adjusts confidence based on context:
- State-critical operations (transfers, storage): 0.55 confidence
- Account data operands with multiplication: 0.70 confidence
- Generic variable arithmetic: 0.30-0.45 confidence
- Deduplication: Limits to one finding per statement to avoid duplicate reports for nested expressions.
Limitations
False positives: Arithmetic on values known to be bounded at the application level (e.g., percentages 0-100) will be flagged. Programs using Anchor’s checked_math! macro are partially recognized through Anchor detection. 32-bit arithmetic variants (Add32, Sub32, etc.) always receive Medium severity. False negatives: Checked arithmetic performed via function calls (e.g., helper functions) is not recognized. Arithmetic in CPI instruction data construction is not analyzed.
Related Detectors
- Integer Overflow — provides deeper analysis with user-controlled data tracking
- Integer Truncation — detects unsafe type casts
- Division by Zero — detects unvalidated divisors
- Unchecked Fee/Rent Math — detects unchecked math in fee calculations