Hawk High

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

State Variable Collision in UUPS Upgrade Causes Data Corruption

Summary

The state variables in the LevelTwo implementation contract are not ordered identically to those in LevelOne, leading to a state variable collision during the UUPS upgrade. This causes the proxy contract to read and write incorrect data when interacting with variables located after the point of divergence in the storage layout, breaking core invariants.

Vulnerability Details

contract LevelOne is Initializable, UUPSUpgradeable {
using SafeERC20 for IERC20;
address principal;
bool inSession;
@> uint256 schoolFees;
@> uint256 public immutable reviewTime = 1 weeks;
@> uint256 public sessionEnd;
@> uint256 public bursary;
@> uint256 public cutOffScore;
contract LevelTwo is Initializable {
using SafeERC20 for IERC20;
address principal;
bool inSession;
@> uint256 public sessionEnd;
@> uint256 public bursary;
@> uint256 public cutOffScore;
@> mapping(address => bool) public isTeacher;
@> mapping(address => bool) public isStudent;

With modified contract (fixing the upgrade), the provided test case test_state_variables_are_the_same_after_graduate() confirms this data corruption:

function test_state_variables_are_the_same_after_graduate() public schoolInSession {
uint256 levelOneCutOffScore = levelOneProxy.cutOffScore();
levelTwoImplementation = new LevelTwo();
levelTwoImplementationAddress = address(levelTwoImplementation);
bytes memory data = abi.encodeCall(LevelTwo.graduate, ());
vm.prank(principal);
levelOneProxy.graduateAndUpgrade(levelTwoImplementationAddress, data);
uint256 levleTwoCutOffScore = LevelTwo(proxyAddress).cutOffScore();
assertEq(levleTwoCutOffScore, levelOneCutOffScore);
}

It will gets a fail test.

Failing tests:
Encountered 1 failing test in test/LeveOnelAndGraduateTest.t.sol:LevelOneAndGraduateTest
[FAIL: assertion failed: 18000000000000000000000 != 70] test_state_variables_are_the_same_after_graduate() (gas: 1656296)

Impact

  • Incorrect Data Reads: Any attempt to access shared state variables in LevelTwo that come after schoolFees in LevelOne's layout will retrieve data belonging to different variables, resulting in incorrect values.

  • Broken Invariants: Invariants tied to these misread state variables (like cutOffScore, bursary, sessionEnd) will be broken, leading to unpredictable and erroneous contract behavior.

  • Logical Errors: Functions in LevelTwo that use these corrupted state variables will operate on bad data, potentially causing severe logical errors, incorrect calculations, or failed transactions.

  • Loss of State Integrity: The contract's internal state becomes unreliable and inconsistent with the intended data representation.

Tools Used

Recommendations

To fix this, ensure the state variable layout in LevelTwo perfectly matches the layout of LevelOne for all variables that exist in both contracts, up to the point where new variables are added in LevelTwo.

Updates

Lead Judging Commences

yeahchibyke Lead Judge 28 days 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.