Uninitialized Local Storage
Overview
- Severity: High
- Confidence: High
- Affected Versions: < 0.5.0
What is the Uninitialized Local Storage vulnerability?
In the context of Solidity, reference variables may have different classifications of data location, indicating that the referenced data is an in-memory instance or a reference to stored data that is part of the state of the Ethereum Virtual Machine (EVM). For variables that reference storage locations, uninitialized variables will by default point to the first storage slot of the referenced smart contract, which may contain other data. In earlier versions of Solidity, the compiler would allow contracts making such references to compile, which could lead to inadvertent corruption of the contract state.
Further reading: Solidity Documentation: Data location
Technical example of vulnerable code
// SPDX-License-Identifier: Unlicense
pragma solidity 0.4.10;
contract UserProfileManager {
struct UserProfile {
string name;
uint256 balance;
}
mapping(address => UserProfile) public userProfiles;
// Function to initialize a user profile
function createUserProfile(address user, string name, uint256 initialBalance) external {
userProfiles[user] = UserProfile(name, initialBalance);
}
// Function intended to update only the user's balance,
// but incorrectly manipulates a local storage variable leading to unintended behavior
function updateUserBalance(address user, uint256 newBalance) external {
// Uninitialized local storage variable intended to point to a user's profile
UserProfile storage userProfile; // This line is missing initialization!
// The developer intended to update only the balance, not realizing `userProfile` points to slot 0
userProfile.balance = newBalance; // This inadvertently overwrites the first slot of the contract storage
}
// Additional smart contract functionality omitted for brevity
}
In the example above, contract UserProfileManager
utilizes a custom struct, UserProfile
, to manage certain state data. In function updateUserBalance()
, a reference to a user profile in storage is created, but is not initialized. Modifying the storage data pointed to by this reference can subsequently corrupt the state of the contract.
Technical example of how to fix the vulnerability
// SPDX-License-Identifier: Unlicense
pragma solidity 0.4.10;
contract UserProfileManagerUpdated {
struct UserProfile {
string name;
uint256 balance;
}
mapping(address => UserProfile) public userProfiles;
// Function to initialize a user profile
function createUserProfile(address user, string name, uint256 initialBalance) external {
userProfiles[user] = UserProfile(name, initialBalance);
}
function updateUserBalance(address user, uint256 newBalance) external {
UserProfile storage userProfile = userProfiles[user];
userProfile.balance = newBalance;
}
// Additional smart contract functionality omitted for brevity
}
In the revised example above, contract UserProfileManagerUpdated
now properly assigns the reference in function updateUserBalance
to a specific instance of a UserProfile
struct, avoiding the problem associated with the default pointer location.