• YouTube Channel
  • System Status
  • VS Code Extension
  • Enum Conversion Out of Range

    Overview

    What is the Enum Conversion Out of Range vulnerability?

    In prior versions of the Solidity language, enum types could be cast from values that did not necessarily fit within the range of the enum. Even in newer versions of the language, it is always a good practice to check the potential range of the enum if casting from a variable.

    Further reading: Solidity Documentation: Enums

    Technical example of vulnerable code

      // SPDX-License-Identifier: Unlicense
      pragma solidity 0.4.0;
    
      contract LoanManager {
          enum LoanState { Requested, Approved, Funded, Closed }
    
          struct Loan {
              uint256 id;
              uint256 amount;
              LoanState state;
          }
    
          mapping(uint256 => Loan) public loans;
          uint256 public nextLoanId;
    
          // Function to create a new loan request
          function requestLoan(uint256 amount) public {
              loans[nextLoanId] = Loan(nextLoanId, amount, LoanState.Requested);
              nextLoanId++;
           }
    
          // Administrative function to update loan state based on external input
          // This function does not validate the new state, risking an out-of-range cast
          function updateLoanState(uint256 loanId, uint256 newState) public {
              // Risky cast from uint256 to LoanState without range validation
              loans[loanId].state = LoanState(newState); // Potential for errors with out-of-range values
          }
      }
    
    

    In the example above, contract LoanManager uses an enum LoanState to track the lifecycle of loans (for example, as part of a DeFi application). The function updateLoanState() casts the updated state from a raw uint256 type, without first checking if it could fit within the range, potentially leading to errors or undesired behavior.

    Technical example of how to fix the vulnerability

      // SPDX-License-Identifier: Unlicense
      pragma solidity 0.8.0;
    
      contract LoanManagerUpdated {
          enum LoanState { Requested, Approved, Funded, Closed }
    
          struct Loan {
              uint256 id;
              uint256 amount;
              LoanState state;
          }
    
          mapping(uint256 => Loan) public loans;
          uint256 public nextLoanId;
    
          // Function to create a new loan request
          function requestLoan(uint256 amount) public {
              loans[nextLoanId] = Loan(nextLoanId, amount, LoanState.Requested);
              nextLoanId++;
          }
    
          // Administrative function to update loan state based on external input
          function updateLoanState(uint256 loanId, uint256 newState) public {
              require(newState <= uint256(LoanState.Closed), "Invalid state"); // Validate the new state
              loans[loanId].state = LoanState(newState);
          }
      }
    
    

    In the revised example above, a newer version of the compiler is specified, and contract LoanManagerUpdated also explicitly checks the value of newState inside of updateLoanState() to make sure it fits within the enum range.