One of the most common vulnerability found in Smart Contract is the overflow and underflow problem. In modern high-level programming languages like JavaScript or Python it's not anymore a problem, but in others like C++ or Solidity this problem exists.
An overflow occurs when a variable is incremented above its maximum value, resulting in the minimum number representable.
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+ 0x000000000000000000000000000000000001
----------------------------------------
= 0x000000000000000000000000000000000000
An underflow happens when an unsigned number is decremented below zero, resulting in the maximum possible value.
0x000000000000000000000000000000000000
- 0x000000000000000000000000000000000001
----------------------------------------
= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
Integers in the Ethereum Virtual Machine have a specified size that determines the range of values they can represent: for example the type uint8
can only represent numbers between 0 and 255, if we try to store the value 256
in an uint8 variable it will result 0
.
If variables and calculations are not checked it's possible to make a contract perform operations it shouldn't do through unexpected logic flows.
contract TimeLock {
mapping(address => uint) public balances;
mapping(address => uint) public lockTime;
function deposit() public payable {
balances[msg.sender] += msg.value;
lockTime[msg.sender] = now + 10 years;
}
function increaseLockTime(uint _secondsToIncrease) public {
lockTime[msg.sender] += _secondsToIncrease;
}
function withdraw() public {
require(balances[msg.sender] > 0);
require(now > lockTime[msg.sender]);
msg.sender.transfer(balances[msg.sender]);
balances[msg.sender] = 0;
}
}
This contract stores your ether for at least 10 years from when the Ethers were received.
By looking at the contract code it's possible to notice that there is no check on the value provided in the increaseLockTime
function that influence the lockTime
of the funds: it's a simple addition without any overflow check. lockTime
value is an uint256, so its maximum value is 2^256
.
If we call the increaseLockTime
function and give 2^256 - 1
as a parameter the lockTime
variable will contain a value less that now
, so the current timestamp.
It's then possible to simply call the withdraw
function and obtain the ether deposited.
OpenZeppelin create a collection of libraries for secure smart contract development that contains the SafeMath library, very useful for doing math operations without overflow and underflow problems. With Solidity 0.8 the error was solved by throwing and error when an overflow or underflow happens.