Integer overflow/underflow
SCWE-047: Integer Overflows and Underflows
Theory
Before Solidity 0.8.0, arithmetic operations (+, -, *) on integers did not perform overflow or underflow checks.
When a value exceeded its maximum or minimum range, it would wrap around silently.
# Example with uint8 (0–255 range):
## Overflow (wrap-around upwards)
255 + 1 → 0
## Underflow (wrap-around downwards)
0 - 1 → 255This behavior allowed attackers to manipulate balances, bypass restrictions, or break security-critical logic such as token transfer limits, balance updates, access control checks, time or block-based conditions.
Practice
When auditing contracts written in Solidity before version 0.8.0, every arithmetic operation must be reviewed manually, because the compiler does not check for overflow or underflow. Look for any place where user input influences additions, subtractions, multiplications, or counters.
Typical high-risk areas include balance updates, token mint or burn logic, counters, and time-based expressions (such as now + duration). If user-controlled values interact with these variables, the contract may become exploitable through arithmetic wrap-around
Even in modern Solidity versions, auditors must pay attention to unchecked { ... } blocks, custom arithmetic libraries, and inline assembly using add, sub, or mul. These cases bypass compiler safety and can behave like old Solidity math.
A simple token-like contract that subtracts user balance without overflow checks:
pragma solidity ^0.6.0;
contract Token {
mapping(address => uint256) public balance;
function transfer(uint256 amount) public {
require(balance[msg.sender] >= amount);
balance[msg.sender] -= amount; // Underflow vulnerability
balance[tx.origin] += amount;
}
}If an attacker has a balance of 0, calling transfer(1) causes the subtraction to wrap around and produce the maximum possible uint256 value. The attacker instantly becomes the richest holder.
cast send $CONTRACT_ADDRESS \
"transfer(uint256)" 1 \
--rpc-url $RPC_URL \
--private-key $PK \
-vv