• YouTube Channel
  • System Status
  • VS Code Extension
  • Downcast of Number to Address

    Overview

    What is the Downcast of Number to Address vulnerability?

    Solidity, like many other programming languages, supports explicit type casts made by the developer. In earlier versions of Solidity, it was possible to make a direct narrowing cast to an address type from a wider numeric type, which could potentially result in incorrect address values due to truncated bits and faulty assumptions about the data being cast.

    Further reading: Solidity Documentation: Explicit Type Conversions

    Technical example of vulnerable code

      // SPDX-License-Identifier: Unlicense
      pragma solidity ^0.7.0;
    
      contract CalldataHandler {
          // Function that directly processes calldata to perform an action
          function processCalldata() external payable {
              // Load calldata into temporary variables
              uint256 dataSlice1;
              uint256 dataSlice2;
    
              assembly {
                  dataSlice1 := calldataload(0)  // Assuming specific calldata structure
                  dataSlice2 := calldataload(32) // for demonstration purposes
              }
    
              // Perform some action with these data, e.g. transfer ether
              address targetAddress = address(dataSlice1);
              uint256 amount = dataSlice2;
    
              (bool success, ) = payable(targetAddress).call{value: amount}("");
              require(success, "Failed to send value");
          }
    
          // Ensure the contract can receive Ether
          receive() external payable {}
      }
    
    

    In the above example, contract CalldataHandler has functionality for directly accessing calldata associated with function calls to processCalldata rather than using parameter passing. N.B. that for simplifying purposes, the calldata are assumed to start with the two variables of interest, in this case an address and an amount of ether to send to it. However, the address here is extracted from data stored as a uint256 variable, which may truncate important bits and cause ether to be sent to an incorrect and possible irrecoverable address.

    Technical example of how to fix the vulnerability

      // SPDX-License-Identifier: Unlicense
      pragma solidity ^0.7.0;
    
      contract CalldataHandlerUpdated {
          // Function that directly processes calldata to perform an action
          function processCalldata() external payable {
              // Initialize a variable to store the extracted address
              address targetAddress;
              // Load calldata into temporary variable for the amount
              uint256 amount;
    
              assembly {
                  // Load the first 20 bytes of calldata into a uint160 variable
                  // Solidity automatically pads this on the right, fitting the address format
                  targetAddress := and(calldataload(0), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
                  amount := calldataload(32)
              }
    
              // Perform the action with the extracted data, e.g., transfer Ether
              (bool success, ) = payable(targetAddress).call{value: amount}("");
              require(success, "Failed to send value");
          }
    
          // Ensure the contract can receive Ether
          receive() external payable {}
      }
    
    

    In the revised example above, contract CalldataHandlerUpdated now loads only 20 bytes of data into an address variable directly, rather than converting from a uint256 and then making a narrowing downcast. N.B. the same simplifying assumption regarding the positioning of the variables in the calldata applies in this example as well.