• YouTube Channel
  • System Status
  • VS Code Extension
  • Unchecked Block with Subtraction

    Overview

    What is the Unchecked Block with Subtraction vulnerability?

    In modern versions of Solidity, arithmetic operations that would underflow or overflow will now revert by default. However, it is possible to obtain the previous behavior where such operations will wrap rather than reverting, by using an unchecked block. While arithmetic operations inside such blocks may incur lower gas costs, these blocks should be used carefully, as unintended wrapping behavior may lead to program errors if the developer erroneously believes an operation to be safe.

    Further reading: Solidity Documentation: Checked or Unchecked Arithmetic

    Technical example of vulnerable code

      // SPDX-License-Identifier: Unlicense
      pragma solidity ^0.8.0;
    
      contract SimpleTokenWallet {
          mapping(address => uint256) public balances;
    
          // Functionality for interfacing with a token standard, e.g. ERC-20, omitted for brevity
    
          // Allow users to deposit tokens to their wallet
          function deposit(uint256 _amount) external {
              require(_amount > 0, "Deposit amount must be greater than 0");
              balances[msg.sender] = balances[msg.sender] + _amount;
    
              // Logic to manage token transfer omitted for brevity
          }
    
          // Unsafe withdrawal function with potential for underflow
          function withdraw(uint256 _amount) external {
              // Dangerous: unchecked subtraction can underflow
              unchecked {
                  balances[msg.sender] = balances[msg.sender] - _amount;
              }
    
              // Logic to manage token transfer omitted for brevity
          }
      }
    
    

    In the example above, contract SimpleTokenWallet represents a simplified means of managing user tokens, e.g. ERC-20 tokens. The function withdraw() uses unchecked arithmetic to reduce a user's token balance when tokens are withdrawn from the wallet, which could potentially underflow and cause the balances mapping to incorrectly credit an extremely high balance for a user. Depending on how the contract managed tokens overall, this could potentially allow a user to withdraw tokens deposited by others, counter to intended smart contract logic.

    Technical example of how to fix the vulnerability

      // SPDX-License-Identifier: Unlicense
      pragma solidity ^0.8.0;
    
      contract SimpleTokenWalletUpdated {
          mapping(address => uint256) public balances;
    
          // Functionality for interfacing with a token standard, e.g. ERC-20, omitted for brevity
    
          // Allow users to deposit tokens to their wallet
          function deposit(uint256 _amount) external {
              require(_amount > 0, "Deposit amount must be greater than 0");
              balances[msg.sender] = balances[msg.sender] + _amount;
    
              // Logic to manage token transfer omitted for brevity
          }
    
          function withdraw(uint256 _amount) external {
              balances[msg.sender] = balances[msg.sender] - _amount;
    
              // Logic to manage token transfer omitted for brevity
          }
      }
    
    

    In the updated example above, contract SimpleTokenWalletUpdated uses a modern version of Solidity where underflow will revert, and does not use unchecked arithmetic in the withdraw() function. It is important to note that if using an earlier versions of Solidity for some reason, the developer would be required to check the arithmetic themselves to ensure it could not underflow in practice.