What is Integer Overflow/Underflow?
Integer overflow occurs when an arithmetic operation produces a value that exceeds the maximum value the data type can hold. Underflow is the opposite - when a value goes below the minimum (usually zero for unsigned integers).
📊 uint256 Limits
That's 115,792,089,237,316,195,423,570,985,008,687,907,853,269,984,665,640,564,039,457,584,007,913,129,639,935
Good News!
unchecked blocks and older contracts.How Overflow/Underflow Works
🔄 Overflow Example (uint8)
uint8 max value: 255
255 + 1 = 0 (wraps around!)
255 + 10 = 9
🔄 Underflow Example (uint8)
uint8 min value: 0
0 - 1 = 255 (wraps around!)
5 - 10 = 251
1// In Solidity < 0.8.0, this would NOT revert
2contract OverflowExample {
3 // uint8 can hold values 0-255
4 uint8 public value = 255;
5
6 function increment() public {
7 value += 1; // Before 0.8: value becomes 0
8 // After 0.8: REVERTS!
9 }
10}
11
12contract UnderflowExample {
13 uint256 public balance = 100;
14
15 function withdraw(uint256 amount) public {
16 // Before 0.8: If amount > balance, result wraps to huge number
17 // e.g., 100 - 200 = 2^256 - 100 (a massive number!)
18 balance -= amount;
19 }
20}Real Attack: BEC Token (2018)
The Beauty Chain (BEC) token had a critical overflow vulnerability in its batch transfer function:
1// The vulnerable batchTransfer function (simplified)
2function batchTransfer(address[] _receivers, uint256 _value) public {
3 uint256 cnt = _receivers.length;
4
5 // VULNERABLE: This multiplication can overflow!
6 uint256 amount = uint256(cnt) * _value;
7
8 // If overflow happens, 'amount' becomes tiny
9 // but each receiver still gets full '_value'
10 require(_value > 0 && balances[msg.sender] >= amount);
11
12 balances[msg.sender] -= amount; // Deducts tiny overflowed amount
13
14 for (uint256 i = 0; i < cnt; i++) {
15 balances[_receivers[i]] += _value; // Adds full _value to each!
16 }
17}
18
19// Attack:
20// _receivers.length = 2
21// _value = 2^255 (half of max uint256)
22// amount = 2 * 2^255 = 2^256 = 0 (overflow!)
23// Result: Sender loses 0, receivers gain 2^255 each!Impact
Solidity 0.8+ Protection
Starting with Solidity 0.8.0, all arithmetic operations check for overflow/underflow and revert if detected. This is built into the compiler.
1// Solidity 0.8.0+
2contract SafeByDefault {
3 uint256 public value = type(uint256).max; // Maximum value
4
5 function tryOverflow() public {
6 value += 1; // REVERTS with Panic(0x11)
7 }
8}
9
10// If you NEED unchecked math (for gas savings), be explicit:
11contract UncheckedMath {
12 function riskyIncrement(uint256 x) public pure returns (uint256) {
13 // WARNING: This can overflow!
14 unchecked {
15 return x + 1; // Does NOT revert on overflow
16 }
17 }
18
19 // Common safe use case: loop counters
20 function safeLoop(uint256[] calldata data) public pure returns (uint256 sum) {
21 for (uint256 i = 0; i < data.length;) {
22 sum += data[i];
23 unchecked {
24 ++i; // Safe: i can't overflow before running out of gas
25 }
26 }
27 }
28}When You're Still Vulnerable
⚠️ unchecked Blocks
Code inside unchecked { } bypasses overflow protection.
⚠️ Older Contracts
Contracts compiled with Solidity <0.8.0 are still vulnerable.
⚠️ Type Casting
Downcasting (uint256 to uint8) can silently truncate values.
⚠️ Assembly
Inline assembly bypasses all Solidity safety checks.
1// Type casting vulnerability (even in Solidity 0.8+)
2contract CastingIssue {
3 function unsafeCast(uint256 bigNumber) public pure returns (uint8) {
4 // WARNING: This truncates without reverting!
5 // 256 becomes 0, 257 becomes 1, etc.
6 return uint8(bigNumber);
7 }
8
9 // Safe alternative
10 function safeCast(uint256 bigNumber) public pure returns (uint8) {
11 require(bigNumber <= type(uint8).max, "Value too large");
12 return uint8(bigNumber);
13 }
14}
15
16// OpenZeppelin SafeCast library
17import "@openzeppelin/contracts/utils/math/SafeCast.sol";
18
19contract WithSafeCast {
20 using SafeCast for uint256;
21
22 function safeCastExample(uint256 value) public pure returns (uint8) {
23 return value.toUint8(); // Reverts if value > 255
24 }
25}Best Practices
✅ Use Solidity 0.8.0+
Always use the latest Solidity version to get built-in overflow protection.
✅ Use SafeCast for Downcasting
OpenZeppelin's SafeCast library provides safe type conversions.
✅ Review unchecked Blocks Carefully
Only use unchecked when you've mathematically proven overflow is impossible.
✅ Validate Inputs
Check that multiplication/addition results are reasonable before using them.