• YouTube Channel
  • System Status
  • VS Code Extension
  • Shadowing State

    Overview

    What is the Shadowing State vulnerability?

    Solidity's namespaces allow for shadowing of variable names in outer scopes, similar to many other programming languages. Due to the financial nature of many Ethereum applications written in Solidity, confusion of variables due to scoping could lead to potentially serious consequences. While newer versions of the compiler may generate warnings about such shadowing, it will not prevent smart contracts containing shadowed variables from compiling, so developers should take care in these cases.

    Further reading: Solidity Documentation: Scoping and Declarations

    Technical example of vulnerable code

      // SPDX-License-Identifier: Unlicense
      pragma solidity ^0.8.0;
    
      contract AccountManager {
          // State variable to track total balances across all accounts
          uint256 public totalBalance;
    
          // Mapping to store individual account balances
          mapping(address => uint256) public accountBalances;
    
          constructor() {
              totalBalance = 0;
          }
    
          // Function to deposit funds into an account
          function deposit(uint256 amount) public {
              accountBalances[msg.sender] += amount;
    
              // Local variable that shadows the state variable `totalBalance`
              uint256 totalBalance = accountBalances[msg.sender];
    
              // The intention might have been to update the state variable `totalBalance`, 
              // but instead, this only affects the local scope variable `totalBalance`.
              totalBalance += amount;
    
              // Logical error: The state variable `totalBalance` remains unchanged,
              // leading to an incorrect total balance tracking.
          }
    
          // Function to check the total balance stored in the contract
          // This will not reflect deposits correctly due to the shadowing issue above.
          function checkTotalBalance() public view returns (uint256) {
              return totalBalance;
          }
      }
    
    

    In the example above, contract AccountManager represents functionality for managing user account balances in a nominal decentralized finance (DeFi) application. The contract manages a state variable totalBalance to track overall user balances, and has a function deposit() which is meant to use this variable in its accounting. However, the developer has (presumably inadvertently) shadowed the state variable declaration of totalBalance within this function, which will cause checkTotalBalance() to return inaccurate figures.

    Technical example of how to fix the vulnerability

      // SPDX-License-Identifier: Unlicense
      pragma solidity ^0.8.0;
    
      contract AccountManagerUpdated {
          // State variable to track total balances across all accounts
          uint256 public totalBalance;
    
          // Mapping to store individual account balances
          mapping(address => uint256) public accountBalances;
    
          constructor() {
              totalBalance = 0;
          }
    
          // Function to deposit funds into an account
          function deposit(uint256 amount) public {
              accountBalances[msg.sender] += amount;
              totalBalance += amount;
          }
    
          // Function to check the total balance stored in the contract
          function checkTotalBalance() public view returns (uint256) {
              return totalBalance;
          }
      }
    
    

    In the revised example above, contract AccountManagerUpdated now references the totalBalance state variable directly in the deposit() function, as the developer intended.