Shadowing State
Overview
- Severity: Low
- Confidence: High
- Affected Versions: All
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.