Hawk High

First Flight #39
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: medium
Valid

Missing Storage Gap in Upgradeable Contract

Issue Description

The LevelOne contract inherits from Initializable and UUPSUpgradeable but fails to implement a storage gap (__gap) variable. In upgradeable contracts, storage gaps are essential safeguards that reserve storage slots for future versions, preventing storage collision during upgrades.

Detailed Analysis

The UUPS (Universal Upgradeable Proxy Standard) pattern allows contracts to be upgraded while preserving their state. However, this requires careful management of the storage layout. When a contract is upgraded:

  1. The storage layout of the new implementation must be compatible with the existing storage.

  2. Adding new variables to a parent contract will push down all child contract variables in storage.

In the current implementation:

contract LevelOne is Initializable, UUPSUpgradeable {
// State variables
address principal;
bool inSession;
// ... other variables
// No __gap variable is defined
}

OpenZeppelin's upgradeable contracts include a __gap variable to reserve storage slots:

uint256[50] private __gap;

But LevelOne does not implement its own storage gap. If future versions of Initializable or UUPSUpgradeable add new storage variables, they will collide with LevelOne's storage.

Reproduction Scenario

  1. Deploy LevelOne as a proxy.

  2. Users interact with the contract, storing critical data.

  3. OpenZeppelin releases a new version of UUPSUpgradeable with additional state variables.

  4. The contract is upgraded to use the new OpenZeppelin contracts.

  5. The new state variables from UUPSUpgradeable now occupy the same slots as the beginning of LevelOne's state.

  6. This causes principal, inSession, and possibly other critical variables to be corrupted.

Recommendation

Add a storage gap at the end of the contract:

contract LevelOne is Initializable, UUPSUpgradeable {
// Existing code...
// Reserve storage slots for future versions
uint256[50] private __gap;
}

This reserves 50 storage slots for potential parent contract additions, significantly reducing the risk of storage collisions during upgrades.

Updates

Lead Judging Commences

yeahchibyke Lead Judge 6 months ago
Submission Judgement Published
Validated
Assigned finding tags:

storage collision

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.