Integer overflow and underflow are common vulnerabilities in smart contracts that can lead to serious security issues. These vulnerabilities occur when a contract performs an operation on an integer that results in a value outside the range of the integer data type. In this article, we’ll explore what integer overflow and underflow are and how to prevent them in your smart contracts.
What is Integer Overflow?
Integer overflow occurs when a contract performs an operation on an integer that results in a value larger than the maximum value that can be represented by the integer data type. In Solidity, the maximum value for an uint256
integer is 2^256 - 1
, or 115792089237316195423570985008687907853269984665640564039457584007913129639935
. If an operation on an uint256
integer results in a value larger than this maximum, an integer overflow will occur.
Here is an example of a contract that is vulnerable to integer overflow:
pragma solidity ^0.6.0;
contract IntegerOverflow {
uint public balance;
function deposit() public payable {
balance += msg.value;
}
}
In this contract, the deposit
function increments the balance
variable by the value of the ether sent to the contract. If the balance
variable is already at the maximum value for an uint256
integer and the contract receives more ether, an integer overflow will occur and the balance
variable will be reset to zero.
What is Integer Underflow?
Integer underflow occurs when a contract performs an operation on an integer that results in a value smaller than the minimum value that can be represented by the integer data type. In Solidity, the minimum value for an uint256
integer is zero. If an operation on an uint256
integer results in a value smaller than zero, an integer underflow will occur.
Here is an example of a contract that is vulnerable to integer underflow:
pragma solidity ^0.6.0;
contract IntegerUnderflow {
uint public balance;
function withdraw(uint amount) public {
balance -= amount;
}
}
In this contract, the withdraw
function decrements the balance
variable by a specified amount. If the balance
variable is already at zero and the contract receives a request to withdraw more ether, an integer underflow will occur and the balance
variable will be set to the maximum value for an uint256
integer.
How to Prevent Integer Overflow and Underflow
There are several ways to prevent integer overflow and underflow in your smart contracts. One common method is to use the require
function to check the values of the integers before performing an operation on them.
Here is an example of a contract that uses the require
function to prevent integer overflow:
pragma solidity ^0.6.0;
contract SafeFromIntegerOverflow {
uint public balance;
function deposit() public payable {
require(balance + msg.value >= balance);
balance += msg.value;
}
}
In this contract, the deposit
function uses the require
function to check that the result of balance + msg.value
is greater than or equal to the current value of balance
. If the result is not greater than or equal to the current value of balance
, the require
function will throw an exception and prevent the integer overflow.
Here is an example of a contract that uses the require
function to prevent integer underflow:
pragma solidity ^0.6.0;
contract SafeFromIntegerUnderflow {
uint public balance;
function withdraw(uint amount) public {
require(balance >= amount);
balance -= amount;
}
}
In this contract, the withdraw
function uses the require
function to check that the current value of balance
is greater than or equal to the requested withdrawal amount. If the current value of balance
is not greater than or equal to the requested withdrawal amount, the require
function will throw an exception and prevent the integer underflow.
Another method to prevent integer overflow and underflow is to use the SafeMath library, which is a library of math functions that include built-in checks for integer overflow and underflow.
Here is an example of a contract that uses the SafeMath library to prevent integer overflow:
pragma solidity ^0.6.0;
import "https://github.com/OpenZeppelin/openzeppelin-solidity/contracts/math/SafeMath.sol";
contract SafeFromIntegerOverflow {
using SafeMath for uint;
uint public balance;
function deposit() public payable {
balance = balance.add(msg.value);
}
}
In this contract, the deposit
function uses the add
function from the SafeMath library to add the value of the ether sent to the contract to the balance
variable. The add
function includes a built-in check for integer overflow and will throw an exception if an overflow occurs.
Here is an example of a contract that uses the SafeMath library to prevent integer underflow:
pragma solidity ^0.6.0;
import "https://github.com/OpenZeppelin/openzeppelin-solidity/contracts/math/SafeMath.sol";
contract SafeFromIntegerUnderflow {
using SafeMath for uint;
uint public balance;
function withdraw(uint amount) public {
balance = balance.sub(amount);
}
}
In this contract, the withdraw
function uses the sub
function from the SafeMath library to subtract the requested withdrawal amount from the balance
variable. The sub
function includes a built-in check for integer underflow and will throw an exception if an underflow occurs.
Conclusion
Integer overflow and underflow are common vulnerabilities in smart contracts that can lead to serious security issues. By using the require
function or the SafeMath library, you can ensure that your contracts are not vulnerable to these attacks and maintain their security. It is important to properly handle integer operations in order to prevent overflow and underflow and keep your smart contracts secure.
Exercises
To review these concepts, we will go through a series of exercises designed to test your understanding and apply what you have learned.
Write a Solidity contract that defines a function named deposit
that is vulnerable to integer overflow. The function should increment a public variable named balance
by the value of the ether sent to the contract.
pragma solidity ^0.6.0;
contract IntegerOverflow {
uint public balance;
function deposit() public payable {
balance += msg.value;
}
}
Modify the IntegerOverflow
contract from Exercise 1 to use the require
function to prevent integer overflow.
pragma solidity ^0.6.0;
contract SafeFromIntegerOverflow {
uint public balance;
function deposit() public payable {
require(balance + msg.value >= balance);
balance += msg.value;
}
}
Write a Solidity contract that defines a function named withdraw
that is vulnerable to integer underflow. The function should decrement a public variable named balance
by a specified amount.
pragma solidity ^0.6.0;
contract IntegerUnderflow {
uint public balance;
function withdraw(uint amount) public {
balance -= amount;
}
}
Modify the IntegerUnderflow
contract from Exercise 3 to use the require
function to prevent integer underflow.
pragma solidity ^0.6.0;
contract SafeFromIntegerUnderflow {
uint public balance;
function withdraw(uint amount) public {
require(balance >= amount);
balance -= amount;
}
}
Write a Solidity contract that uses the SafeMath library to prevent integer overflow and underflow. The contract should define a function named deposit
that increments a public variable named balance
by the value of the ether sent to the contract, and a function named withdraw
that decrements the balance
variable by a specified amount.
pragma solidity ^0.6.0;
import "https://github.com/OpenZeppelin/openzeppelin-solidity/contracts/math/SafeMath.sol";
contract SafeMathExample {
using SafeMath for uint;
uint public balance;
function deposit() public payable {
balance = balance.add(msg.value);
}
function withdraw(uint amount) public {
balance = balance.sub(amount);
}
}