Integer Truncation
Detects unsafe integer type casts that may truncate data, causing loss of significant bits and security vulnerabilities.
Integer Truncation
Overview
The integer truncation detector identifies unsafe type casts from larger to smaller integer types (e.g., u64 to u32, u16, or u8) without bounds checking. When values exceed the destination type’s range, they are silently truncated, leading to data loss, incorrect computations, and security vulnerabilities. For remediation steps, see the Integer Truncation Remediation.
Why This Is an Issue
Solana programs frequently work with u64 values for lamport amounts, token balances, and account sizes. When these values are cast to smaller types without validation, truncation occurs silently:
- Token amount manipulation: Casting
u64value0x1_0000_0000tou32produces0. An attacker who controls the input amount can craft values that truncate to small or zero values. - Access control bypass: User IDs or role values truncated to
u8can wrap to match privileged role constants. - Balance errors: Account balances truncated before storage or comparison produce incorrect financial results.
- Size miscalculation: Account data sizes truncated from
u64tou8wrap around, causing buffer overflows or underflows.
The detector assigns higher severity when the truncated expression involves syscall results (likely balances or system values) or memory loads (external data), and when the truncation ratio is large (e.g., u64 to u8).
How to Resolve
// Before: Vulnerable -- u64 to u32 without check
let lamports: u64 = account.lamports();
let amount: u32 = lamports as u32; // Truncates if > u32::MAX
// After: Fixed -- bounds check before cast
let lamports: u64 = account.lamports();
let amount: u32 = u32::try_from(lamports)
.map_err(|_| ProgramError::ArithmeticOverflow)?;
Examples
Vulnerable Code
pub fn process_withdrawal(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
// u64 amount cast to u16 -- truncates values over 65535
let display_amount: u16 = amount as u16;
log_withdrawal(display_amount);
// Logic uses truncated value for balance check
let balance: u64 = **accounts[0].try_borrow_lamports()?;
if (balance as u32) >= amount as u32 { // Both truncated!
**accounts[0].try_borrow_mut_lamports()? -= amount;
**accounts[1].try_borrow_mut_lamports()? += amount;
}
Ok(())
}
Fixed Code
pub fn process_withdrawal(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
let balance: u64 = **accounts[0].try_borrow_lamports()?;
// Use full-width types throughout
if balance >= amount {
**accounts[0].try_borrow_mut_lamports()? -= amount;
**accounts[1].try_borrow_mut_lamports()? += amount;
} else {
return Err(ProgramError::InsufficientFunds);
}
Ok(())
}
Example JSON Finding
{
"detector": "integer-truncation",
"severity": "high",
"confidence": 0.85,
"title": "Unsafe Integer Type Truncation",
"description": "Type cast from 8-byte to 2-byte integer without bounds checking.",
"cwe_ids": [197]
}
Detection Methodology
- Cast identification: Scans all expressions for
Castoperations where the destination type is a smaller integer than the source. - Size inference: Infers source expression size from constants, variable defaults (u64), load sizes, and syscall returns.
- Bounds check search: Looks for comparison operations on the same variable before the cast, indicating a bounds validation pattern.
- Severity calibration: Assigns higher severity for large truncation ratios (u64 to u8/u16) and expressions involving syscalls or memory loads.
Limitations
False positives: Intentional truncation for display purposes or bit manipulation will be flagged. Programs that validate bounds in called functions or use saturating casts (value.min(u32::MAX as u64) as u32) may not be recognized as safe. False negatives: Truncation via bitwise AND masking (rather than type casts) is detected by the hash collision detector instead. Truncation in expressions passed directly to function calls without assignment may be missed.
Related Detectors
- Critical Truncation — detects truncation flowing into financial operations
- Integer Overflow — detects arithmetic overflow vulnerabilities
- Unchecked Arithmetic — detects missing checked math