Lesson 11 of 23
In Progress

Integer Overflow and Underflow

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);
    }
}