Integer Overflow Remediation
How to eliminate integer overflow and underflow vulnerabilities in Solidity by using Solidity 0.8+ checked arithmetic and safe unchecked block practices.
Integer Overflow Remediation
Overview
Related Detector: Integer Overflow
Integer overflow and underflow occur when arithmetic exceeds the bounds of the integer type. In Solidity versions before 0.8.0, this silently wraps around. In Solidity 0.8+, arithmetic reverts on overflow — except inside unchecked {} blocks. The primary fix is to upgrade to Solidity 0.8+ and avoid unchecked blocks on user-controlled values.
Recommended Fix
Before (Vulnerable — Pre-0.8 Solidity)
pragma solidity ^0.6.0;
contract VulnerableToken {
mapping(address => uint256) balances;
// VULNERABLE: overflow wraps silently — balances[sender] - amount underflows
// to 2^256 - 1 if amount > balance
function transfer(address to, uint256 amount) external {
balances[msg.sender] -= amount; // Underflow!
balances[to] += amount;
}
}
After (Fixed — Solidity 0.8+)
pragma solidity ^0.8.0;
contract SafeToken {
mapping(address => uint256) balances;
// FIXED: Solidity 0.8+ automatically reverts on underflow
function transfer(address to, uint256 amount) external {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount; // Safe: protected by require above + 0.8+ check
balances[to] += amount;
}
}
Alternative Mitigations
1. Safe unchecked Block Usage (Gas Optimization)
When you need gas efficiency and overflow is provably impossible, use unchecked with explicit documentation:
pragma solidity ^0.8.0;
contract OptimizedContract {
function sumArray(uint256[] calldata values) external pure returns (uint256 total) {
// SAFE unchecked: loop counter cannot overflow uint256 within block gas limit
for (uint256 i = 0; i < values.length; ) {
total += values[i]; // This is NOT safe unchecked without bounds validation
unchecked { ++i; } // SAFE: i < values.length guarantees no overflow
}
}
function subtractWithCheck(uint256 a, uint256 b) external pure returns (uint256) {
// SAFE: explicit comparison before unchecked subtraction
require(a >= b, "Underflow would occur");
unchecked { return a - b; } // Safe because a >= b is verified
}
}
2. OpenZeppelin SafeMath (Legacy — for Pre-0.8 Codebases)
For contracts that must support Solidity < 0.8:
pragma solidity ^0.6.0;
import "@openzeppelin/contracts/math/SafeMath.sol";
contract SafeToken {
using SafeMath for uint256;
mapping(address => uint256) balances;
function transfer(address to, uint256 amount) external {
balances[msg.sender] = balances[msg.sender].sub(amount, "Insufficient balance");
balances[to] = balances[to].add(amount);
}
}
3. Fixed-Point Arithmetic for Financial Calculations
For fractional amounts (interest rates, fees), use fixed-point with explicit scaling:
pragma solidity ^0.8.0;
contract FixedPointFee {
uint256 private constant PRECISION = 1e18;
uint256 private constant FEE_RATE = 3e15; // 0.3% = 3/1000 = 3e15 / 1e18
function calculateFee(uint256 amount) public pure returns (uint256) {
// Multiply then divide — avoids precision loss from division-first
// The multiplication is safe because amount is bounded by token supply
return (amount * FEE_RATE) / PRECISION;
}
}
4. Use OpenZeppelin Math for Checked Multiplication
For products that might overflow before being divided:
import "@openzeppelin/contracts/utils/math/Math.sol";
// Safe for amounts up to 2^128 (prevents mulDiv overflow)
uint256 result = Math.mulDiv(a, b, denominator);
Common Mistakes
Mistake 1: Unchecked Block on User-Controlled Input
// WRONG: unchecked on user-supplied amount
function deposit(uint256 amount) external {
unchecked {
// If amount = 2^256 - balance, totalDeposits wraps to 0
totalDeposits += amount; // VULNERABLE inside unchecked
}
}
Mistake 2: Type Casting Truncation
// WRONG: casting larger type to smaller silently truncates
uint256 largeAmount = type(uint256).max;
uint128 truncated = uint128(largeAmount); // Silently becomes type(uint128).max
// FIXED: check before casting
require(largeAmount <= type(uint128).max, "Value too large");
uint128 safe = uint128(largeAmount);
Mistake 3: Multiplication Before Addition in Compound Expressions
// WRONG: intermediate multiplication can overflow even if the final result fits
uint256 result = a * b + c * d; // a * b might overflow before addition
// FIXED: use mulDiv or break into safe steps
uint256 ab = Math.mulDiv(a, b, 1); // Checked multiply
uint256 cd = Math.mulDiv(c, d, 1);
uint256 result = ab + cd;